bartabs.tcl
bartabs.tcl

bartabs.tcl

  • bartabs.tcl
  • NS bartabs
    • messageBox messageBox : Runs Tk's or apave's ok/yes/no/cancel dialogue. type - ok, yesno or yesnocancel ttl - title ttl - message args - additional arguments of tk_messageBox Returns 1 if 'yes' chosen, 2 if 'no', 0 otherwise. try the apave package's dialogue
  •   EONS bartabs
  • bartabs class hierarchy
  • Tab
  •   Private methods of Tab
    • My My : Creates a caller of method. ID - ID of caller
    • ID ID : Returns ID of caller.
    • IDs IDs : Returns a pair of TID and BID.
    • Tab_Create Tab_Create : Creates a tab widget (frame, label, button). w - parent frame text - tab's label Returns a list of created widgets of the tab.
    • Tab_create Tab_create : Creates tab method and registers it. Defined by "My".
    • Tab_ExpandOption Tab_ExpandOption : Gets a real -expand option, counting that it may be set as a number>1 meaning "starting from this number do expanding, otherwise not" expand - original value of -expand option
    • Tab_cget Tab_cget : Gets options of tab. args - list of options Returns a list of values or one value if args is one option.
    • Tab_configure Tab_configure : Sets values of options for a tab. args - list of pairs "option value"
    • Tab_DictItem Tab_DictItem : Gets item data from a tab item (ID + data). TID - tab ID or the tab item (ID + data). data - tab's data (list of option-value) If 'data' omitted, TID is a tab item (ID + data). If the tab's attribute is absent, it's meant to be "". Returns a list of values: ID, text, wb, wb1, wb2, pf.
    • Tab_ItemDict Tab_ItemDict : Returns a tab item (ID + data) from item data. text - tab's text; wb - tab's frame widget wb1 - tab's label widget wb2 - tab's button widget pf - "p" for tab packed, "" for tab forgotten
    • Tab_Data Tab_Data : Creates data of new tab. text - new tab's label The bar is checked for a duplicate of 'text'. Returns a tab item or "" (if duplicated).
    • Tab_BID Tab_BID : Gets BID from TID. act - if "check", only checks the existance of TID If 'act' is "check" and a bar not found, -1 is returned, otherwise BID. Returns a list of 1. BID (or -1 if no bar found) 2. index of the tab in tab list 3. the tab data.
    • Tab_Bindings Tab_Bindings : Sets bindings on events of tabs.
    • Tab_Font Tab_Font : Gets a font attributes for tab label.
    • Tab_MarkAttrs Tab_MarkAttrs : Gets image & mark attributes of marks. TID - ID of current tab withbg - if true, gets also background wb2 - tab's button Returns string of attributes if any.
    • Tab_SelAttrs Tab_SelAttrs : Gets font attributes of selected tab. fnt - original font attributes fgsel - foreground for selection bgsel - background for selection If both set, fgsel and bgsel mean colors If bgsel=="", fgsel!="", fgsel is a widget to get attributes If fgsel=="", 'selection' is 'underlining'
    • Tab_MarkBar Tab_MarkBar : Marks the tabs of a bar . TID - ID of the current tab
    • Tab_MarkBars Tab_MarkBars : Marks the tabs. BID - bar ID (if omitted, all bars are scanned) TID - ID of the current tab
    • Tab_TextEllipsed Tab_TextEllipsed : Returns a tab's label and tip. text - label lneed - label length anyway
    • Tab_Iconic Tab_Iconic : Gets a flag "tabs with icons". Returns "yes", if tabs are supplied with icons.
    • Tab_Pack Tab_Pack : Packs a tab widget. wb, wb1, wb2 - tab's widgets
    • Tab_RemoveLinks Tab_RemoveLinks : Removes a tab's links to lists.
    • Tab_Is Tab_Is : Checks if 'wb' is an existing tab widget. wb - path
    • Tab_CloseFew Tab_CloseFew : Closes tabs of bar. TID - ID of the current tab or -1 if to close all left - "yes" if to close all at left of TID, "no" if at right args - options (if contains -skipsel, selected tabs aren't closed)
    • PrepareCmd PrepareCmd : Prepares a command bound to an action on a tab. opt - command option (-csel, -cmov, -cdel) args - additional argumens of the command The commands can include wildcards: %b for bar ID, %t for tab ID, %l for tab label. Returns "" or the command if 'opt' exists in 'args'.
    • Tab_Cmd Tab_Cmd : Executes a command bound to an action on a tab. opt - command option (-csel, -cmov, -cdel) args - additional argumens of the command The commands can include wildcards: %b for bar ID, %t for tab ID, %l for tab label. Returns 1, if no command set; otherwise: 1 for Yes, 0 for No, -1 for Cancel.
    • Tab_BeCurrent Tab_BeCurrent : Makes the tab be currently visible.
    • Disabled Disabled : Checks if the tab is disabled.
  •   Event handlers
    • DestroyMoveWindow DestroyMoveWindow : Destroys the moving window zombi.
    • OnEnterTab OnEnterTab : Handles the mouse pointer entering a tab. wb1, wb2 - tab's widgets fgo, bgo - colors of "mouse over the tab"
    • OnLeaveTab OnLeaveTab : Handles the mouse pointer leaving a tab. wb1, wb2 - tab's widgets
    • OnButtonPress OnButtonPress : Handles the mouse clicking a tab. wb1 - tab's label x - x position of the mouse pointer
    • OnButtonMotion OnButtonMotion : Handles the mouse moving over a tab. wb - tab's frame wb1 - tab's label x, y - positions of the mouse pointer
    • OnButtonRelease OnButtonRelease : Handles the mouse releasing a tab. wb1o - original tab's label x - x position of the mouse pointer
    • OnCtrlClick OnCtrlClick : Handles a selection of tabs with Ctrl+click.
    • OnPopup OnPopup : Handles the mouse right-clicking on a tab. X, Y - positions of the mouse pointer
  •   Public methods of Tab
    • show show : Shows a tab in a bar and sets it current. refill - if "yes", update the bar lifo - if "yes", allows moving a tab to 0th position When refill=no and lifo=no, just shows a tab in its current position.
    • close close : Closes a tab and updates the bar. redraw - if "yes", update the bar and select the new tab args - additional argumens of the -cdel command Returns "1" if the deletion was successful, otherwise 0 (no) or -1 (cancel).
    • visible visible : Checks if a tab is visible. Returns yes if the tab is visible,.
  •   EOC Tab
  • Bar
  •   Private methods of Bar
    • Bar_Data Bar_Data : Puts data of new bar in btData. barOptions - new bar's options Returns BID of new bar.
    • Bar_DefaultMenu Bar_DefaultMenu : Creates default menu items. popName - variable name for popup's data
    • Bar_MenuList Bar_MenuList : Tunes "List" menu item for colors & underlining. popi - menu of tab items ilist - list of "s" (separators) and TIDs pop - menu to be themed in apave package
    • Bar_Cmd2 Bar_Cmd2 : Executes a command after an action. comopt2 - the command's option (-csel2, -cdel2, -cmov2)
    • Mc_MenuItems Mc_MenuItems : Returns localized menu items' label.
    • InitColors InitColors : Initializes colors of a bar.
    • Style Style : Sets styles a bar's widgets.
    • ScrollCurr ScrollCurr : Scrolls the current tab to the left/right. dir - 1/-1 for scrolling to the right/left
    • ArrowsState ArrowsState : Sets a state of scrolling arrows. tleft, tright - index of left/right tab sright - state of a right arrow ("no" for disabled)
    • FillMenuList FillMenuList : Fills "List of tabs" item of popup menu. popi - menu of tab items TID - clicked tab ID mnu - root menu mustBeSorted - flag "sorted list" Return a list of items types: s (separator) and TID.
    • Width Width : Calculates and returns the bar width to place tabs.
    • FillFromLeft FillFromLeft : Fills a bar with tabs from the left to the right (as much tabs as possible). ileft - index of a left tab tright - index of a right tab
    • FillFromRight FillFromRight : Fills a bar with tabs from the right to the left (as much tabs as possible). tleft, tright - index of left/right tab behind - flag "go behind the right tab"
    • Locked Locked : Checks for "draw locked" mode: protects the menu.
    • Refill Refill : Fills a bar with tabs. itab - index of tab left - if "yes", fill from left to right behind - flag "go behind the right tab"
    • CheckDsblPopup CheckDsblPopup : Controls disabling of Close* menu items. mnuit - menu label Returns "yes" for disabled menu item
    • NeedDraw NeedDraw : Redraws a bar at need.
  •   Exported methods of Bar
    • _runBound_ _runBound_ : Runs a method bound to an event occuring at a widget. w - widget ev - event args - the bound method & its arguments
  •   Auxiliary methods of Bar
    • Aux_WidgetWidth Aux_WidgetWidth : Calculates a widget's width.
    • Aux_InitDraw Aux_InitDraw : Auxiliary method used before cycles drawing tabs. Returns a list of main options of drawing tabs.
    • Aux_CheckTabVisible Aux_CheckTabVisible : Auxiliary method used to check if a tab is visible.
    • Aux_EndDraw Aux_EndDraw : Auxiliary method used after cycles drawing tabs.
    • Aux_IndexInList Aux_IndexInList : Searches ID in list.
  •   Public methods of Bar
    • cget cget : Gets values of options of bars & tabs. args - list of options, e.g. {-tabcurrent -MyOpt} Return a list of values or one value if args is one option.
    • configure configure : Sets values of options for bars & tabs. args - list of pairs "option value"
    • draw draw : Draws the bar tabs at slight changes. upd - if "yes", run "update" before redrawing
    • update update : Updates the bar in hard way.
    • clear clear : Forgets (hides) the shown tabs.
    • scrollLeft scrollLeft : Scrolls tabs to the left.
    • scrollRight scrollRight : Scrolls tabs to the right.
    • listTab listTab : Gets a list of tabs. Returns a list of TID, text, wb, wb1, wb2, pf.
    • comparetext comparetext : Compares items (by -text attribute) for sort method. it1 - 1st item to compare it2 - 2nd item to compare See also: sort
    • sort sort : Sorts a list of tabs by the tab names. mode - option of sort cmd - command to compare two items
    • listFlag listFlag : Gets a list of TID + flags "visible", "marked", "selected", "disabled". filter - "" for all or "v","m","s","d" for visible, marked, selected, disabled Returns a list "TID, text, visible, marked, selected, disabled" for all or a list of TID for filtered.
    • insertTab insertTab : Inserts a new tab into a bar. txt - tab's label pos - tab's position in tab list img - tab's image Returns TID of new tab or "".
    • tabID tabID : Gets TID by tab's label. txt - label Returns TID or -1.
    • popList popList : Shows a menu of tabs. X - x coordinate of mouse pointer Y - y coordinate of mouse pointer sortedList - flag "sorted list"
    • remove remove : Removes a bar. Returns "yes" at success.
    • moveTab moveTab : Moves a tab to a new position in the bar. pos - the new position
    • checkDisabledMenu checkDisabledMenu : Checks whether the popup menu's items are disabled. func - close function *func* equals to: 1 - for "Close All" 2 - for "Close All at Left" 3 - for "Close All at Right" Returns "yes" if the menu's item is disabled.
    • closeAll closeAll : Closes tabs of bar. func - close function *func* equals to: 1 - for "Close All" 2 - for "Close All at Left" 3 - for "Close All at Right"
    • bindToEvent bindToEvent : Binds an event on a widget to a command. w - the widget's path event - the event args - the command
  •   EOC Bar
  • Bars
  •   Methods of Bars
    • constructor constructor
    • destructor destructor
  •   Private methods of Bars
    • Bars_Method Bars_Method : Executes a method for all bars. mtd - method's name args - method's arguments
    • MarkTab MarkTab : Sets option of tab(s). opt - option args - list of TID
    • UnmarkTab UnmarkTab : Unsets option of tab(s). opt - option args - list of TID
    • TtkTheme TtkTheme : Checks if a standard ttk theme is used.
  •   Public methods of Bars
    • create create : Creates a bar. barCom - bar command's name or barOpts barOpts - list of bar's options tab1 - tab to show after creating the bar Returns BID.
    • updateAll updateAll : Updates all bars in hard way.
    • drawAll drawAll : Redraws all bars. upd - if "yes", run "update" before redrawing
    • removeAll removeAll : Removes all bars.
    • markTab markTab : Marks tab(s). args - list of TID
    • unmarkTab unmarkTab : Unmarks tab(s). args - list of TID or {}
    • onSelectCmd onSelectCmd : Runs a command (set by "-csel3") on a list of tabs. args - list of TID
    • selectTab selectTab : Selects tab(s). args - list of TID
    • unselectTab unselectTab : Unselects tab(s). args - list of TID or {}
    • enableTab enableTab : Enables tab(s). args - list of TID or {}
    • disableTab disableTab : Disables tab(s). args - list of TID
    • isTab isTab : Checks if a tab exists. TID - tab ID Returns true if the tab exists.
    • MoveTab MoveTab : Changes a tab's position in bar. TID1 - TID of the moved tab TID2 - TID of a tab to move TID1 behind TID1 and TID2 must be of the same bar.
    • moveSelTab moveSelTab : Changes a tab's or selected tabs' position in bar. TID1 - TID of the moved tab TID2 - TID of a tab to move TID1 behind TID1 and TID2 must be of the same bar.
  •   EOC Bars
  • EOF
###########################################################
# Name:    bartabs.tcl
# Author:  Alex Plotnikov  (aplsimple@gmail.com)
# Date:    01/12/2023
# Brief:   Handles the tab bar widget.
# License: MIT.
###########################################################

package provide bartabs 1.6.10

# ________________________ NS bartabs _________________________ #

namespace eval ::bartabs {

  # IDs for new bars & tabs
  variable NewBarID -1 NewTabID -1 NewTabNo -1
  variable NewAfterID;  array set NewAfterID [list]

  # images made by base64
  image create photo bts_ImgLeft \
  -data {iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAElBMVEUAAABJSUmSkpJtbW22trbb
29vYK8X/AAAAAXRSTlMAQObYZgAAAEBJREFUCNdjAANGBigQhNKMjlCGEJTBqAplCIVCGIwqKopg
hrATjKGkZAiRMgIyIEJABlTIEGYDjMEoiGQp3BkAc58E+W1dC9QAAAAASUVORK5CYII=}
  image create photo bts_ImgRight \
  -data {iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAElBMVEUAAABJSUmSkpJtbW22trbb
29vYK8X/AAAAAXRSTlMAQObYZgAAAEBJREFUCNdjYGAQYIACQRhDRADGUIQxggSgjFCokJCTkwCU
oWIIZggrKcEYygIQBlAAwgAKQBiCMLsE0C2FCAAAa1IEzBjs2sUAAAAASUVORK5CYII=}
  image create photo bts_ImgNone \
  -data {iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA1BMVEUAAACnej3aAAAAAXRSTlMA
QObYZgAAAAtJREFUCNdjIBEAAAAwAAFletZ8AAAAAElFTkSuQmCC}
  image create photo bts_ImgClose \
  -data {iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAALVBMVEUAAAAAACTb29u2trbt4+Ll
4eHw4eD/6N/y3Nv85N/94t3m29vn0dPjz8/dyssim+gAAAAAAXRSTlMAQObYZgAAAEdJREFUCNdj
wAvaFZiWgWgORSUlYQcgg11QSFFQAchgUhQUFLoAkjskKKgNVqwkKKjHAJMCqeGEKWYzBGoPAMk5
KTCp4rURAEWmB5A5tzUJAAAAAElFTkSuQmCC}

  variable BarsList [list]
; proc drawAll {} {
    # Draws all bars. Used at updating themes etc.
    foreach bars $::bartabs::BarsList {$bars drawAll}
  }
  #_______________________

  proc messageBox {type ttl msg args} {
    # Runs Tk's or apave's ok/yes/no/cancel dialogue.
    #  type - ok, yesno or yesnocancel
    #  ttl - title
    #  ttl - message
    # args - additional arguments of tk_messageBox
    # Returns 1 if 'yes' chosen, 2 if 'no', 0 otherwise.

    # try the apave package's dialogue
    if {[catch {set res [::apave::obj $type ques $ttl $msg]}]} {
      # or run the standard tk_messageBox
      set res [tk_messageBox -title $ttl -message $msg -type $type \
        -icon question {*}$args]
      set res [expr {$res eq {yes} ? 1 : ($res eq {no} ? 2 : 0)}]
    }
    return $res
  }

  ## ____________ EONS bartabs ____________ ##

}

# ____________ bartabs class hierarchy ____________ #

oo::class create ::bartabs::Tab {
}

oo::class create ::bartabs::Bar {
  superclass ::bartabs::Tab
}

oo::class create ::bartabs::Bars {
  superclass ::bartabs::Bar
}

# ________________________ Tab _______________________ #

## ____________ Private methods of Tab ____________ ##

oo::define ::bartabs::Tab {

method My {ID} {
# Creates a caller of method.
#   ID - ID of caller

  set t [string range $ID 0 2]
  oo::objdefine [self] "method $ID {args} { \
    set m \[lindex \$args 0\] ; \
    if {\$m in {{} -1}} {return {}} ; \
    if {\$m eq {create} && {$t} eq {bar} || \$m in {cget configure} && {$t} eq {tab}} { \
    set args \[lreplace \$args 0 0 Tab_\$m\]} ; \
    return \[my {*}\$args\]}"
}
#_______________________

method ID {} {
# Returns ID of caller.

  lindex [uplevel 1 {self caller}] 2
}
#_______________________

method IDs {TID} {
# Returns a pair of TID and BID.

  list $TID [my $TID cget -BID]
}
#_______________________

method Tab_Create {BID TID w text} {
# Creates a tab widget (frame, label, button).
#   w - parent frame
#   text - tab's label
# Returns a list of created widgets of the tab.

  lassign [my $BID cget -relief -bd -padx -pady -BGMAIN] relief bd padx pady bgm
  lassign [my $TID cget -wb -wb1 -wb2] wb wb1 wb2
  if {!$bd} {set relief flat}
  if {![my Tab_Is $wb]} {
    if {$wb eq {}} {
      set ::bartabs::NewTabNo [expr {($::bartabs::NewTabNo+1)%1000000}]
      set wb $w.$TID[format %06d $::bartabs::NewTabNo]
      set wb1 $wb.l
      set wb2 $wb.b
    }
    my $TID configure -wb $wb -wb1 $wb1 -wb2 $wb2
    ttk::frame $wb -borderwidth [expr {$bd? $bd : 2}]
    ttk::label $wb1
    if {[my TtkTheme]} {
      ttk::button $wb2 -style ClButton$BID -image bts_ImgNone \
        -command [list [self] $TID close yes -withicon yes] -takefocus 0
    } else {
      button $wb2 -relief flat -borderwidth 0 -highlightthickness 0 -image bts_ImgNone \
        -command [list [self] $TID close yes -withicon yes] -takefocus 0 -background $bgm
    }
  }
  $wb configure -relief $relief
  $wb1 configure -relief flat -padding "$padx $pady $padx $pady" \
    {*}[my Tab_Font $BID]
  lassign [my Tab_TextEllipsed $BID $text] text ttip
  if {[set tip [my $TID cget -tip]] ne {}} {
    my $TID configure -tip $tip  ;# run baltip after creating $wb1 & $wb2
  }
  $wb1 configure -text $text -background $bgm
  if {[my Tab_Iconic $BID]} {
    $wb2 configure -state normal
  } else {
    $wb2 configure -state disabled -image {}
  }
  list $wb $wb1 $wb2
}
#_______________________

method Tab_create {tabCom label} {
  # Creates tab method and registers it. Defined by "My".

  set BID [my ID]
  if {[set TID [my $BID tabID $label]] eq {}} {
    return -code error "No label {$label} in $BID"
  }
; proc $tabCom {args} "return \[[self] $TID {*}\$args\]"
  set lObj [my $BID cget -TABCOM]
  my $BID configure -TABCOM [lappend lObj [list $TID $tabCom]]
}
#_______________________

method Tab_ExpandOption {BID expand} {
  # Gets a real -expand option, counting that it may be set as a number>1
  # meaning "starting from this number do expanding, otherwise not"
  #   expand - original value of -expand option

  if {[string is digit $expand] && $expand>1} {
    set tabs [my $BID cget -TABS]
    set expand [expr {$expand<[llength $tabs]}]
  }
  return $expand
}
#_______________________

method Tab_cget {args} {
# Gets options of tab.
#   args - list of options
# Returns a list of values or one value if args is one option.

  variable btData
  lassign [my Tab_BID [set TID [my ID]]] BID i tab
  lassign $tab tID tdata
  set res [list]
  foreach opt $args {
    switch -- $opt {
      -BID {lappend res $BID}
      -text - -wb - -wb1 - -wb2 - -pf {
        if {[catch {lappend res [dict get $tdata $opt]}]} {
          lappend res {}
        }
      }
      -index {
        if {$i<([dict get $btData $BID -LLEN]-1)} {
          lappend res $i
        } else {
          lappend res end
        }
      }
      -width {  ;# width of tab widget
        lassign [my Tab_DictItem $tab] tID text wb wb1 wb2
        if {![my Tab_Is $wb]} {
          lappend res 0
        } else {
          set b1 [ttk::style configure TLabel -borderwidth]
          if {$b1 eq {}} {set b1 0}
          lassign [my $BID cget -bd -expand -static] bd expand static
          set bd [expr {$bd?2*$b1:0}]
          set b2 [expr {[my Aux_WidgetWidth $wb2]-3}]
          set expand [my Tab_ExpandOption $BID $expand]
          set expand [expr {$expand||![my Tab_Iconic $BID]?2:0}]
          lappend res [expr {[my Aux_WidgetWidth $wb1]+$b2+$bd+$expand}]
        }
      }
      default {  ;# user's options
        if {[catch {lappend res [dict get $tdata $opt]}]} {lappend res {}}
      }
    }
  }
  if {[llength $args]==1} {return [lindex $res 0]}
  return $res
}
#_______________________

method Tab_configure {args} {
# Sets values of options for a tab.
#   args - list of pairs "option value"

  lassign [my Tab_BID [set TID [my ID]]] BID i tab
  lassign $tab tID data
  foreach {opt val} $args {
    dict set data $opt $val
    if {$opt eq {-tip}} {   ;# configure the tab's tip
      lassign [my $TID cget -wb1 -wb2] wb1 wb2
      if {$wb1 ne {}} {
        catch {
          baltip::tip $wb1 $val -under 3
          baltip::tip $wb2 $val -under 3
        }
      }
    }
  }
  set tab [list $TID $data]
  my $BID configure -TABS [lreplace [my $BID cget -TABS] $i $i $tab]
}
#_______________________

method Tab_DictItem {TID {data ""}} {
# Gets item data from a tab item (ID + data).
#   TID - tab ID or the tab item (ID + data).
#   data - tab's data (list of option-value)
# If 'data' omitted, TID is a tab item (ID + data).
# If the tab's attribute is absent, it's meant to be "".
# Returns a list of values: ID, text, wb, wb1, wb2, pf.

  if {$data eq {}} {lassign $TID TID data}
  set res [list $TID]
  foreach a {-text -wb -wb1 -wb2 -pf} {
    if {[dict exists $data $a]} {
      lappend res [dict get $data $a]
    } else {
      lappend res {}
    }
  }
  return $res
}
#_______________________

method Tab_ItemDict {TID text {wb ""} {wb1 ""} {wb2 ""} {pf ""}} {
# Returns a tab item (ID + data) from item data.
#   text - tab's text;
#   wb - tab's frame widget
#   wb1 - tab's label widget
#   wb2 - tab's button widget
#   pf - "p" for tab packed, "" for tab forgotten

  list $TID [list -text $text -wb $wb -wb1 $wb1 -wb2 $wb2 -pf $pf]
}
#_______________________

method Tab_Data {BID text} {
# Creates data of new tab.
#   text - new tab's label
# The bar is checked for a duplicate of 'text'.
# Returns a tab item or "" (if duplicated).

  variable btData
  if {[dict exists $btData $BID] && [my $BID tabID $text] ne {}} {return {}}
  my My tab[incr ::bartabs::NewTabID]
  my Tab_ItemDict tab$::bartabs::NewTabID $text
}
#_______________________

method Tab_BID {TID {act ""}} {
# Gets BID from TID.
#   act - if "check", only checks the existance of TID
# If 'act' is "check" and a bar not found, -1 is returned, otherwise BID.
# Returns a list of 1. BID (or -1 if no bar found) 2. index of the tab in tab list 3. the tab data.

  variable btData
  set BID {}
  dict for {bID bInfo} $btData {
    set tabs [my $bID cget -TABS]
    if {[set i [my Aux_IndexInList $TID $tabs]] > -1} {
      set BID $bID
      break
    }
  }
  if {$act eq {check}} {return $BID}
  if {$BID eq {}} {
    return -code error "bartabs: tab ID $TID not found in the bars"
  }
  list $BID $i [lindex $tabs $i]
}
#_______________________

method Tab_Bindings {BID} {
# Sets bindings on events of tabs.

  lassign [my $BID cget -static -FGOVER -BGOVER -WWID] static fgo bgo wwid
  foreach tab [my $BID listTab] {
    lassign $tab TID text wb wb1 wb2
    if {[my Tab_Is $wb]} {
      set bar "[self] $BID"
      set tab "[self] $TID"
      set ctrlBP "$tab OnCtrlClick ; break"
      foreach w [list $wb $wb1 $wb2] {
        bind $w <Enter> "$bar OnEnterTab $TID $wb1 $wb2 $fgo $bgo"
        bind $w <Leave> "[self] $TID OnLeaveTab $wb1 $wb2"
        bind $w <Button-3> "[self] $TID OnPopup %X %Y"
        bind $w <Control-ButtonPress> $ctrlBP
      }
      bind $wb <Control-ButtonPress> $ctrlBP
      bind $wb <ButtonPress> "[self] $BID OnButtonPress $TID $wb1 {}"
      bind $wb1 <ButtonPress> "[self] $BID OnButtonPress $TID $wb1 %x"
      bind $wb1 <ButtonRelease> "[self] $BID OnButtonRelease $wb1 %x"
      bind $wb1 <Motion> "[self] $BID OnButtonMotion $wb $wb1 %x %y"
    }
  }
  bind [lindex $wwid 0] <Button-3> "[self] $BID OnPopup %X %Y $BID"
}
#_______________________

method Tab_Font {BID} {
# Gets a font attributes for tab label.

  set font [my $BID cget -font]
  if {$font eq {}} {
    if {[set font [ttk::style configure TLabel -font]] eq {}} {
      set font TkDefaultFont
    }
    set font [font actual $font]
  }
  return "-font {$font}"
}
#_______________________

method Tab_MarkAttrs {BID TID {withbg yes} {wb2 ""}} {
# Gets image & mark attributes of marks.
#   TID - ID of current tab
#   withbg - if true, gets also background
#   wb2 - tab's button
# Returns string of attributes if any.

  lassign [my $BID cget \
    -mark -imagemark -fgmark -bgmark -IMAGETABS -FGMAIN -BGMAIN -FGDSBL -BGDSBL] \
    marktabs imagemark fgm bgm imagetabs fgmain bgmain fgdsbl bgdsbl
  set res {}
  if {[my Disabled $TID]} {
    set imagemark {}
    if {$wb2 ne {}} {$wb2 configure -state disabled}
    set res " -foreground $fgdsbl"
    if {$withbg} {append res " -background $bgdsbl"}
  } elseif {[lsearch $marktabs $TID]>-1} {
    if {$imagemark eq {}} {
      if {$fgm eq {}} {set fgm $fgmain}  ;# empty value - no markable tabs
      set res " -foreground $fgm"
      if {$withbg} {
        if {$bgm eq {}} {set bgm $bgmain}
        append res " -background $bgm"
      }
      if {$wb2 ne {}} {$wb2 configure -image bts_ImgNone}
    }
  } else {
    set imagemark {}
    if {[set i [lsearch -index 0 $imagetabs $TID]]>-1} {
      set imagemark [lindex $imagetabs $i 1]
    } elseif {$wb2 ne {}} {
      $wb2 configure -image bts_ImgNone
    }
  }
  if {$imagemark ne {}} {
    set res " -image $imagemark"
    if {$wb2 ne {}} {
      $wb2 configure {*}$res
      catch {$wb2 configure -style ClButton$BID}
    }
  }
  return $res
}
#_______________________

method Tab_SelAttrs {fnt fgsel bgsel} {
# Gets font attributes of selected tab.
#   fnt - original font attributes
#   fgsel - foreground for selection
#   bgsel - background for selection
# If both set, fgsel and bgsel mean colors
# If bgsel=="", fgsel!="", fgsel is a widget to get attributes
# If fgsel=="", 'selection' is 'underlining'

  lassign $fnt opt val
  if {$fgsel eq {}} {
    dict set val -underline 1
  } else {
    if {$bgsel eq {}} {
      set bgsel [ttk::style configure $fgsel -selectbackground]
      set fgsel [ttk::style configure $fgsel -selectforeground]
    }
    set opt "-foreground $fgsel -background $bgsel $opt"
  }
  return "$opt {$val}"
}
#_______________________

method Tab_MarkBar {BID {TID "-1"}} {
# Marks the tabs of a bar .
#   TID - ID of the current tab

  lassign [my $BID cget -tabcurrent -fgsel -bgsel -select -FGMAIN -BGMAIN] \
    tID fgs bgs fewsel fgm bgm
  if {$TID in {{} {-1}}} {set TID $tID}
  foreach tab [my $BID listTab] {
    lassign $tab tID text wb wb1 wb2
    if {[my Tab_Is $wb]} {
      set font [my Tab_Font $BID]
      set selected [expr {$tID == $TID || [lsearch $fewsel $tID]>-1}]
      if {$selected} {set font [my Tab_SelAttrs $font $fgs $bgs]}
      $wb1 configure {*}$font
      set attrs [my Tab_MarkAttrs $BID $tID [expr {!$selected}] $wb2]
      if {$attrs ne {} && {-image} ni $attrs } {
        $wb1 configure {*}$attrs
      } elseif {!$selected} {
        $wb1 configure -foreground $fgm -background $bgm
      }
    }
  }
  my $BID configure -tabcurrent $TID
}
#_______________________

method Tab_MarkBars {{BID -1} {TID -1}} {
# Marks the tabs.
#   BID - bar ID (if omitted, all bars are scanned)
#   TID - ID of the current tab

  variable btData
  if {$BID == -1} {
    dict for {BID barOpts} $btData {my Tab_MarkBar $BID}
  } else {
    my Tab_MarkBar $BID $TID
  }
}
#_______________________

method Tab_TextEllipsed {BID text {lneed -1}} {
# Returns a tab's label and tip.
#   text - label
#   lneed - label length anyway

  lassign [my $BID cget -lablen -ELLIPSE] lablen ellipse
  if {$lneed ne -1} {set lablen $lneed}
  if {$lablen && [string length $text]>$lablen} {
    set ttip $text
    set text [string range $text 0 $lablen-1]
    append text $ellipse
  } else {
    set ttip {}
  }
  list $text $ttip
}
#_______________________

method Tab_Iconic {BID} {
# Gets a flag "tabs with icons".
# Returns "yes", if tabs are supplied with icons.

  expr {![my $BID cget -static]}
}
#_______________________

method Tab_Pack {BID TID wb wb1 wb2} {

# Packs a tab widget.
#   wb, wb1, wb2 - tab's widgets
  lassign [my $BID cget -static -expand] static expand
  if {[my Tab_Iconic $BID]} {
    pack $wb1 -side left
    pack $wb2 -side left
  } else {
    pack $wb1 -side left -fill x
    pack forget $wb2
  }
  set expand [my Tab_ExpandOption $BID $expand]
  if {$expand} {
    pack $wb -side left -fill x -expand 1
  } else {
    pack $wb -side left
  }
  my $TID configure -pf "p"
}
#_______________________

method Tab_RemoveLinks {BID TID} {
# Removes a tab's links to lists.

  foreach o {-IMAGETABS -TABCOM -mark -disable -select} {
    set l [my $BID cget $o]
    for {set i 0} {$i>-1} {} {
      if {[set i [lsearch -index 0 $l $TID]]>-1} {
        set l [lreplace $l $i $i]
        my $BID configure $o $l
      }
    }
  }
  my Tab_MarkBars $BID
}
#_______________________

method Tab_Is {wb} {
# Checks if 'wb' is an existing tab widget.
#   wb - path

  expr {$wb ne {} && [winfo exists $wb]}
}
#_______________________

method Tab_CloseFew {{TID -1} {left no} args} {
# Closes tabs of bar.
#   TID - ID of the current tab or -1 if to close all
#   left - "yes" if to close all at left of TID, "no" if at right
#   args - options (if contains -skipsel, selected tabs aren't closed)

  set BID [my ID]
  if {$TID ne {-1}} {lassign [my Tab_BID $TID] BID icur}
  set tabs [my $BID listTab]
  set skipsel [expr {[lsearch $args -skipsel]>-1}]
  set seltabs [my $BID cget -select]
  set doupdate no
  set first 1
  for {set i [llength $tabs]} {$i} {} {
    incr i -1
    set tID [lindex $tabs $i 0]
    if {!$skipsel || $tID ni $seltabs} {
      if {$TID eq {-1} || ($left && $i<$icur) || (!$left && $i>$icur)} {
        if {![set res [my $tID close no -first $first]]} break
        if {$res==1} {set doupdate yes}
        set first 0  ;# -first option is "1" for the very first closed tab
      }
    }
  }
  if {$doupdate} {
    my $BID clear
    if {$TID eq {-1}} {
      my $BID Refill 0 yes
    } else {
      my $BID $TID show yes
    }
  }
}
#_______________________

method PrepareCmd {TID BID opt args} {
# Prepares a command bound to an action on a tab.
#   opt - command option (-csel, -cmov, -cdel)
#   args - additional argumens of the command
# The commands can include wildcards: %b for bar ID, %t for tab ID, %l for tab label.
# Returns "" or the command if 'opt' exists in 'args'.

  variable btData
  if {[dict exists $btData $BID $opt]} {
    set com [dict get $btData $BID $opt]
    if {$TID>-1} {
      set label [my $TID cget -text]
    } else {
      set label {}
    }
    set label [string map {\{ ( \} )} $label]
    lappend com {*}$args
    return [string map [list %b $BID %t $TID %l $label] $com]
  }
  return {}
}

#_______________________

method Tab_Cmd {opt args} {
# Executes a command bound to an action on a tab.
#   opt - command option (-csel, -cmov, -cdel)
#   args - additional argumens of the command
# The commands can include wildcards: %b for bar ID, %t for tab ID, %l for tab label.
# Returns 1, if no command set; otherwise: 1 for Yes, 0 for No, -1 for Cancel.

  lassign [my IDs [my ID]] TID BID
  if {[set com [my PrepareCmd $TID $BID $opt {*}$args]] ne {}} {
    if {[catch {set res [{*}$com]}]} {set res yes}
    if {$res eq {} || !$res} {return 0}
    return $res
  }
  return 1
}

#_______________________

method Tab_BeCurrent {} {
# Makes the tab be currently visible.

  if {[set TID [my ID]] in {{} {-1}} || [my Disabled $TID]} return
  set BID [my $TID cget -BID]
  my $TID Tab_Cmd -csel  ;# command before the selection shown
  my Tab_MarkBar $BID $TID
  if {[set wb2 [my $TID cget -wb2]] ne {} && \
  ![string match *bartabs::* [$wb2 cget -image]] &&
  $TID ni [my $BID listFlag "m"]} {
    $wb2 configure -image bts_ImgNone
  }
  my $BID Bar_Cmd2 -csel2 $TID ;# command after the selection shown
}
#_______________________

method Disabled {TID} {
  # Checks if the tab is disabled.

  set dsbltabs [my [my $TID cget -BID] cget -disable]
  expr {[lsearch $dsbltabs $TID]>-1}
}

## ____________ Event handlers ____________ ##

method DestroyMoveWindow {} {
  # Destroys the moving window zombi.

  set BID [my ID]
  set movWin [lindex [my $BID cget -MOVWIN] 0]
  catch {destroy $movWin}
  my $BID configure -MOVX {} -wb1 {}
}
#_______________________

method OnEnterTab {TID wb1 wb2 fgo bgo} {
# Handles the mouse pointer entering a tab.
#   wb1, wb2 - tab's widgets
#   fgo, bgo - colors of "mouse over the tab"

  if {[my Disabled $TID]} return
  $wb1 configure -foreground $fgo -background $bgo
  if {[my Tab_Iconic [my ID]]} {$wb2 configure -image bts_ImgClose}
}
#_______________________

method OnLeaveTab {wb1 wb2} {
# Handles the mouse pointer leaving a tab.
#   wb1, wb2 - tab's widgets

  lassign [my IDs [my ID]] TID BID
  if {[my Disabled $TID]} return
  if {![winfo exists $wb1]} return
  lassign [my $BID cget -FGMAIN -BGMAIN] fgm bgm
  $wb1 configure -foreground $fgm -background $bgm
  my Tab_MarkBars $BID
  if {"-image" ni [set attrs [my Tab_MarkAttrs $BID $TID 0 $wb2]] && \
  [my Tab_Iconic $BID]} {
    $wb2 configure -image bts_ImgNone
    catch {$wb2 configure -style ClButton$BID}
  }
}
#_______________________

method OnButtonPress {TID wb1 x} {
# Handles the mouse clicking a tab.
#   wb1 - tab's label
#   x - x position of the mouse pointer

  if {[my Disabled $TID]} return
  my [set BID [my ID]] configure -MOVX $x
  if {$TID eq {}} {set TID [my $BID tabID [$wb1 cget -text]]}
  my $TID Tab_BeCurrent
}
#_______________________

method OnButtonMotion {wb wb1 x y} {
# Handles the mouse moving over a tab.
#   wb - tab's frame
#   wb1 - tab's label
#   x, y - positions of the mouse pointer

  lassign [my [set BID [my ID]] cget \
    -static -FGMAIN -FGOVER -BGOVER -MOVWIN -MOVX -MOVX0 -MOVX1 -MOVY0] \
    static fgm fgo bgo movWin movX movx movx1 movY0
  if {$movX eq {} || $static} return
  # dragging the tab
  if {![winfo exists $movWin]} {
    # make the tab's replica to be dragged
    toplevel $movWin
    if {$::tcl_platform(platform) == "windows"} {
      wm attributes $movWin -alpha 0.0
    } else {
      wm withdraw $movWin
    }
    if {[tk windowingsystem] eq "aqua"} {
      ::tk::unsupported::MacWindowStyle style $movWin help none
    } else {
      wm overrideredirect $movWin 1
    }
    set movx [set movx1 $x]
    set movX [expr {[winfo pointerx .]-$x}]
    set movY0 [expr {[winfo pointery .]-$y}]
    label $movWin.label -text [$wb1 cget -text] -relief solid \
      -foreground black -background #7eeeee  {*}[my Tab_Font $BID]
    pack $movWin.label -expand 1 -fill both -ipadx 1
    wm minsize $movWin [winfo reqwidth $movWin.label] [winfo reqheight $wb1]
    set againstLooseFocus "[self] $BID DestroyMoveWindow"
    bind $movWin <Leave> $againstLooseFocus
    bind $movWin <ButtonPress> $againstLooseFocus
    $wb1 configure -foreground $fgm
    my $BID configure -wb1 $wb1 -MOVX1 $movx1 -MOVY0 $movY0
  }
  if {abs([winfo pointery .]-$movY0)>$movY0*.5} {
    my $BID DestroyMoveWindow  ;# too vertical
    return
  }
  lassign [my $BID cget -WWID] wframe wlarr
  lassign [split [winfo geometry $wframe] x+] wflen
  lassign [split [winfo geometry $wlarr] x+] walen
  lassign [split [winfo geometry $wb] x+] wbl - wbx
  if {abs($x-$movx)>1 && ($wflen-$wbx+$movx1+$walen)>$x && ($wbx+$wbl-$movx1+$x)>0} {
    wm geometry $movWin +$movX+$movY0
    if {$::tcl_platform(platform) == "windows"} {
      if {[wm attributes $movWin -alpha] < 0.1} {wm attributes $movWin -alpha 1.0}
    } else {
      catch {wm deiconify $movWin ; raise $movWin}
    }
  }
  my $BID configure -MOVX [expr {$movX+$x-$movx}] -MOVX0 $x
}
#_______________________

method OnButtonRelease {wb1o x} {
# Handles the mouse releasing a tab.
#   wb1o - original tab's label
#   x - x position of the mouse pointer

  lassign [my [set BID [my ID]] cget \
    -MOVWIN -MOVX -MOVX1 -MOVY0 -FGMAIN -wb1 -tleft -tright -wbar -static] \
    movWin movX movx1 movY0 fgm wb1 tleft tright wbar static
  my $BID DestroyMoveWindow
  if {$movX eq {} || $wb1o ne $wb1 || $static} return
  # dropping the tab - find a tab being dropped at
  $wb1 configure -foreground $fgm
  lassign [my Aux_InitDraw $BID no] bwidth vislen bd arrlen llen
  set vislen1 $vislen
  set vlist [list]
  set i 0
  set iw1 -1
  set tabssav [set tabs [my $BID cget -TABS]]
  foreach tab $tabs {
    lassign [my Tab_DictItem $tab] tID text _wb _wb1 _wb2 _pf
    if {$_pf ne {}} {
      if {$_wb1 eq $wb1} {
        set vislen0 $vislen
        set tab1 $tab
        set iw1 $i
        set TID $tID
      }
      set wl [expr {[winfo reqwidth $_wb1]+[winfo reqwidth $_wb2]}]
      lappend vlist [list $i $vislen $wl]
      incr vislen $wl
    }
    incr i
  }
  if {$iw1==-1} return  ;# for sure
  if {[my $TID Tab_Cmd -cmov] ni {"1" "yes" "true"}} return ;# chosen to not move
  set vislen2 [expr {$vislen0+$x-$movx1}]
  foreach vl $vlist {
    lassign $vl i vislen wl
    set rightest [expr {$i==$tright && $vislen2>(10+$vislen)}]
    if {$iw1==($i+1) && $x<0} {incr vislen2 $wl}
    if {($vislen>$vislen2 || $rightest)} {
      set tabs [lreplace $tabs $iw1 $iw1]
      set i [expr {$rightest||$iw1>$i?$i:$i-1}]
      if {$rightest && $i<($llen-1) && $i==$iw1} {incr i}
      set tabs [linsert $tabs $i $tab1]
      set left yes
      if {$rightest} {
        set left no
        set tleft $i
      } elseif {$i<$tleft} {
        set tleft $i
      }
      break
    }
  }
  if {$tabssav ne $tabs} {
    my $BID configure -TABS $tabs
    my $BID Refill $tleft $left
    my $BID Bar_Cmd2 -cmov2 $TID ;# command after the action
  }
}
#_______________________

method OnCtrlClick {} {
# Handles a selection of tabs with Ctrl+click.

  lassign [my IDs [my ID]] TID BID
  lassign [my $BID cget -static -select] static fewsel
  if {$static} return
  if {[set i [lsearch $fewsel $TID]]>-1} {
    set fewsel [lreplace $fewsel $i $i]
  } else {
    lappend fewsel $TID
  }
  my $BID configure -select $fewsel
  my Tab_MarkBar $BID
  my $BID Bar_Cmd2 -csel3 $TID ;# command after the action
}
#_______________________

method OnPopup {X Y {BID "-1"} {TID "-1"} {textcur ""}} {
# Handles the mouse right-clicking on a tab.
#   X, Y - positions of the mouse pointer

  if {$BID eq "-1"} {
    lassign [my IDs [my ID]] TID BID
    set textcur [my $TID cget -text]
  }
  lassign [my $BID cget -wbar -menu -USERMNU -UMNU -TABS -static -hidearrows -WWID] \
    wbar popup usermnu popup0 tabs static hidearr wwid
  if {$static && $hidearr && !$usermnu} {
    lassign $wwid wframe wlarr wrarr
    if {[catch {pack info $wlarr}] && [catch {pack info $wrarr}]} {
      return ;# static absolutely
    }
  }
  set pop $wbar.popupMenu
  if {[winfo exist $pop]} {destroy $pop}
  my $BID configure -LOCKDRAW 1
  menu $pop -tearoff 0
  set ipops [set lpops [list]]
  if {$TID eq "-1"} {
    set popup [list [lindex $popup 0] s {*}$popup0]  ;# let "List" be in
  }
  foreach p $popup {
    lassign $p typ label comm menu dsbl tip var
    if {$menu ne {}} {set popc $pop.$menu} {set popc $pop}
    foreach opt {label comm menu dsbl} {
      set $opt [string map [list %b $BID %t $TID %l $textcur] [set $opt]]
    }
    if {[info commands [lindex $dsbl 0]] ne {}} {
      ;# 0/1/2 image label hotkey
      lassign [{*}$dsbl $BID $TID $label] dsbl comimg comlabel hotk
    } else {
      lassign $dsbl dsbl comimg comlabel hotk
      if {$dsbl ne {}} {set dsbl [expr $dsbl]}
      set dsbl [expr {([string is boolean $dsbl] && $dsbl ne {})?$dsbl:0}]
    }
    if {$dsbl eq {2}} continue  ;# 2 - "hide"; 1 - "disable"; 0 - "normal"
    if {$dsbl} {set dsbl {-state disabled}} {set dsbl {}}
    if {$comimg ne {}} {set comimg "-image $comimg"}
    if {$comlabel ne {}} {set label $comlabel}
    if {$comimg eq {}} {set comimg {-image bts_ImgNone}}
    if {$hotk ne {}} {set hotk "-accelerator $hotk"}
    switch [string index $typ 0] {
      s {$popc add separator}
      c {
        switch [string index $typ 1] {
          o - {} { ;# command
            $popc add command -label $label -command $comm \
              {*}$dsbl -compound left {*}$comimg {*}$hotk
          }
          h { ;# checkbutton
            if {$comm ne {}} {set comm [list -command $comm]}
            $popc add checkbutton -label $label {*}$comm -variable $var
          }
        }
      }
      m {
        if {$menu eq {bartabs_cascade} && !$usermnu && $static} {
          set popc $pop  ;# no user mnu & static: only list of tabs be shown
        } else {
          if {[winfo exist $popc]} {destroy $popc}
          menu $popc -tearoff 0
          set popm [string range $popc 0 [string last . $popc]-1]
          $popm add cascade -label $label -menu $popc \
            {*}$dsbl -compound left {*}$comimg {*}$hotk
        }
        if {[string match {bartabs_cascade*} $menu]} {
          set popi $popc
          lappend lpops $popi
          set ipops [my $BID FillMenuList $BID $popi $TID $menu]
        }
      }
    }
    if {$tip ne {}} {
      catch {baltip::tip $popc $tip -index [$popc index end]}
    }
  }
  if {[llength $lpops]} {
    catch {::apave::obj themePopup $pop}
    my Bar_MenuList $BID $TID $pop ;# main menu
    foreach popi $lpops {my Bar_MenuList $BID $TID $popi $ipops}
    if {$TID ne {-1}} {
      lassign [my $TID cget -wb1 -wb2] wb1 wb2
      bind $pop <Unmap> [list [self] $TID OnLeaveTab $wb1 $wb2]
    }
    my $BID DestroyMoveWindow
    tk_popup $pop $X $Y
  } else {
    my $BID popList $X $Y
  }
  my $BID configure -LOCKDRAW {}
}

## ____________ Public methods of Tab ____________ ##

method show {{refill no} {lifo yes}} {
# Shows a tab in a bar and sets it current.
#   refill - if "yes", update the bar
#   lifo - if "yes", allows moving a tab to 0th position
# When refill=no and lifo=no, just shows a tab in its current position.

  lassign [my IDs [my ID]] TID BID
  if {$refill} {my $BID clear}
  set itab 0
  foreach tab [my $BID listTab]  {
    lassign $tab tID text wb wb1 wb2 pf
    if {$TID eq $tID} {
      set refill [expr {$pf eq {}}]  ;# check if visible
      break
    }
    incr itab
  }
  if {$refill && $lifo && [my $BID cget -lifo] && (![my $TID visible] || \
  [string is true -strict [my $BID cget -lifoest]])} {
    my $BID moveTab $TID 0
    set itab 0
  }
  if {$refill} {my $BID Refill $itab no yes}
  my $TID Tab_BeCurrent
}
#_______________________

method close {{redraw yes} args} {
# Closes a tab and updates the bar.
#   redraw - if "yes", update the bar and select the new tab
#   args - additional argumens of the -cdel command
# Returns "1" if the deletion was successful, otherwise 0 (no) or -1 (cancel).

  lassign [my Tab_BID [set TID [my ID]]] BID icurr tabcurr
  if {[my Disabled $TID]} {
    set ttl [msgcat::mc Closing]
    set t [my $TID cget -text]
    set msg [msgcat::mc "Can't close the disabled\n\"%t\"\n\nClose others?"]
    set msg [string map [list %t $t] $msg]
    return [expr {[::bartabs::messageBox yesno $ttl $msg -icon question]==1}]
  }
  set cdel [my $BID cget -cdel]
  if {$cdel eq {}} {
    set res 1
  } else {
    set cdel [my PrepareCmd $TID $BID -cdel {*}$args]
    if {[catch {set res [{*}$cdel]}]} {
      set res [my $TID Tab_Cmd -cdel {*}$args]
    }
  }
  if {$res ni {1 yes true}} {return $res}
  if {$redraw} {my $BID clear}
  lassign [my $BID cget -TABS -tleft -tright -tabcurrent] tabs tleft tright tcurr
  my Tab_RemoveLinks $BID $TID
  destroy [my $TID cget -wb]
  set tabs [lreplace $tabs $icurr $icurr]
  oo::objdefine [self] [list deletemethod [lindex $tabcurr 0]]
  my $BID configure -TABS $tabs
  if {$redraw} {
    if {$icurr>=$tleft && $icurr<[llength $tabs]} {
      my $BID draw
      my [lindex $tabs $icurr 0] Tab_BeCurrent
    } else {
      if {[set TID [lindex $tabs end 0]] ne {}} {
        my $TID show yes ;# last tab deleted: show the new last if any
      }
    }
  }
  my $BID Bar_Cmd2 -cdel2  ;# command after the action
  return 1
}
#_______________________

method visible {} {
# Checks if a tab is visible.
# Returns yes if the tab is visible,.

  lassign [my IDs [my ID]] TID BID
  lassign [my $BID cget -tleft -tright] tleft tright
  set tabs [my $BID listTab]
  for {set i $tleft} {$i<=$tright} {incr i} {
    if {$TID eq [lindex $tabs $i 0]} {
      return yes
    }
  }
  return no
}

## ________________________ EOC Tab _________________________ ##

}

# ________________________ Bar _________________________ #

## ____________ Private methods of Bar ____________ ##

oo::define ::bartabs::Bar {

method Bar_Data {barOptions} {
# Puts data of new bar in btData.
#   barOptions - new bar's options
# Returns BID of new bar.

  variable btData
  set BID bar[incr ::bartabs::NewBarID]
  # defaults:
  set barOpts [dict create -wbar {}  -wbase {} -wproc {} -static no -lowlist no \
    -hidearrows no -scrollsel yes -lablen 0 -tiplen 0 -tleft 0 -tright end \
    -disable [list] -select [list] -mark [list] -fgmark #800080  -fgsel "." \
    -relief groove -padx 1 -pady 1 -expand 0 -tabcurrent -1 -dotip no \
    -bd 0 -separator 1 -lifo 0 -fg {} -bg {} -popuptip {} -sortlist 0 -comlist {} \
    -ELLIPSE "\u2026" -MOVWIN {.bt_move} -ARRLEN 0 -USERMNU 0 -LLEN 0 -title Tabs]
  set tabinfo [set imagetabs [set popup [list]]]
  my Bar_DefaultMenu $BID popup
  foreach {optnam optval} $barOptions {
    switch -exact -- $optnam {
      -tab - -imagetab {
        if {$optnam eq "-imagetab"} {lassign $optval optval img}
        # no duplicates allowed:
        if {[lsearch -index {1 1} -exact $tabinfo $optval]==-1} {
          lappend tabinfo [set tab [my Tab_Data $BID $optval]]
          dict set barOpts -TABS $tabinfo
          dict set barOpts -LLEN [llength $tabinfo]
          if {$optnam eq "-imagetab"} {
            lappend imagetabs [list [lindex $tab 0] $img]
            dict set barOpts -IMAGETABS $imagetabs
          }
        }
      }
      -menu {
        lappend popup {*}$optval
        dict set barOpts -menu $popup
        dict set barOpts -USERMNU 1
        lappend mnu {*}$optval
        if {[string index [lindex $mnu 0] 0] eq "s"} {set mnu [lrange $mnu 1 end]}
        dict set barOpts -UMNU $mnu
      }
      default {
        dict set barOpts $optnam $optval
      }
    }
  }
  set wbar [dict get $barOpts -wbar]
  if {$wbar eq {}} {return -code error {bartabs: -wbar option is obligatory}}
  set wbase [dict get $barOpts -wbase]
  set wproc [dict get $barOpts -wproc]
  foreach o {-tleft -tright} {
    set v [dict get $barOpts $o]
    set v [expr [string map [list end [llength $tabinfo]-1] $v]]
    dict set barOpts $o $v
  }
  if {$wbase ne {} && $wproc eq {}} {
    dict set barOpts -wproc "expr {\[winfo width $wbase\]-80}" ;# 80 for ornithology
  }
  dict set btData $BID $barOpts
  return $BID
}
#_______________________

method Bar_DefaultMenu {BID popName} {
# Creates default menu items.
#   popName - variable name for popup's data

  upvar 1 $popName pop
  set bar "[self] $BID"
  set dsbl "{$bar CheckDsblPopup}"
  lassign [my Mc_MenuItems] list behind close closeall closeleft closeright
  foreach item [list \
  "m {$list} {} bartabs_cascade" \
  "s {} {} {} $dsbl" \
  "m {BHND} {} bartabs_cascade2 $dsbl" \
  "s {} {} {} $dsbl" \
  "c {$close} {[self] %t close yes -first -1} {} $dsbl" \
  "c {$closeall} {$bar closeAll $BID -1 1} {} $dsbl" \
  "c {$closeleft} {$bar closeAll $BID %t 2} {} $dsbl" \
  "c {$closeright} {$bar closeAll $BID %t 3} {} $dsbl"] {
    lappend pop $item
  }
}
#_______________________

method Bar_MenuList {BID TID popi {ilist ""} {pop ""}} {
# Tunes "List" menu item for colors & underlining.
#   popi - menu of tab items
#   ilist - list of "s" (separators) and TIDs
#   pop - menu to be themed in apave package

  if {$pop eq {}} {set pop $popi}
  catch {::apave::obj themePopup $pop}
  lassign [my $BID cget -tabcurrent -select -FGOVER -BGOVER -lowlist] \
    tabcurr fewsel fgo bgo ll
  if {$ll || [catch {set fs "-size [dict get [$pop cget -font] -size]"}]} {
    if {$ll && [string is digit $ll] && $ll>1} {
      set fs "-size $ll"
    } else {
      set fs {}
    }
  }
  # ALERT: "font actual TkDefaultFont" may be wasteful with tclkits
  set font [list -font "[font actual TkDefaultFont] $fs"]
  set llen [llength $ilist]
  if {[$popi cget -tearoff]} {
    set ito 1
    set TID $tabcurr
  } else {
    set ito 0
  }
  for {set i 0} {$i<$llen} {incr i} {
    if {[set tID [lindex $ilist $i]] eq {s}} continue
    set opts [my Tab_MarkAttrs $BID $tID no]
    if {"-image" ni $opts} {append opts " -image bts_ImgNone"}
    append opts " -compound left"
    if {$tID==$tabcurr || [lsearch $fewsel $tID]>-1} {
      set font2 [my Tab_SelAttrs $font {} {}]
    } else {
      set font2 $font
    }
    append opts " $font2"
    if {$tID==$TID} {append opts " -foreground $fgo -background $bgo"}
    if {[string match *bartabs_cascade2 $popi] && [my Disabled $tID]} {
      append opts " -foreground [my $BID cget -FGMAIN]"  ;# move behind any
    }
    catch {$popi entryconfigure [expr {$i+$ito}] {*}$opts}
  }
}
#_______________________

method Bar_Cmd2 {comopt2 {TID ""}} {
# Executes a command after an action.
#   comopt2 - the command's option (-csel2, -cdel2, -cmov2)

  set BID [my ID]
  if {[set com2 [my $BID cget $comopt2]] ne {}} {
    {*}[string map [list %t $TID] $com2]
  }
}
#_______________________

method Mc_MenuItems {} {
  # Returns localized menu items' label.

  namespace eval ::bartabs {
    return [list [msgcat::mc List] \
                 [msgcat::mc behind] \
                 [msgcat::mc Close] \
                 [msgcat::mc {... All}] \
                 [msgcat::mc {... All at Left}] \
                 [msgcat::mc {... All at Right}]]
  }
}
#_______________________

method InitColors {} {
# Initializes colors of a bar.

  set BID [my ID]
  if {[set fgmain [my $BID cget -fg]] eq {}} {
    set fgmain [ttk::style configure . -foreground]
  }
  if {[set bgmain [my $BID cget -bg]] eq {}} {
    set bgmain [ttk::style configure . -background]
  }
  if {[catch {set fgdsbl [dict get [ttk::style map . -foreground] disabled]}]} {
    set fgdsbl $fgmain
  }
  if {[catch {set bgdsbl [dict get [ttk::style map . -background] disabled]}]} {
    set bgdsbl $bgmain
  }
  if {[catch {
    set fgo [ttk::style map TButton -foreground]
    if {[dict exists $fgo active]} {
      set fgo [dict get $fgo active]
    } else {
      set fgo $fgmain
    }
    set bgo [ttk::style map TButton -background]
    if {[dict exists $bgo active]} {
      set bgo [dict get $bgo active]
    } else {
      set bgo $bgmain
    }
  }]} {
    set bgo $fgmain  ;# reversed
    set fgo $bgmain
    if {$bgo in {black #000000}} {set bgo #444444; set fgo #FFFFFF}
  }
  my $BID configure -FGMAIN $fgmain -BGMAIN $bgmain \
    -FGDSBL $fgdsbl -BGDSBL $bgdsbl -FGOVER $fgo -BGOVER $bgo
  my $BID Style
}
#_______________________

method Style {} {
# Sets styles a bar's widgets.

  set BID [my ID]
  set bg [my $BID cget -BGMAIN]
  ttk::style configure ClButton$BID {*}[ttk::style configure TButton]
  ttk::style configure ClButton$BID -relief flat -padx 0 -bd 0 -highlightthickness 0
  ttk::style map ClButton$BID {*}[ttk::style map TButton]
  ttk::style map ClButton$BID -background [list active $bg !active $bg]
  ttk::style layout ClButton$BID [ttk::style layout TButton]
}
#_______________________

method ScrollCurr {dir} {
# Scrolls the current tab to the left/right.
#   dir - 1/-1 for scrolling to the right/left

  lassign [my [set BID [my ID]] cget -scrollsel -tabcurrent] sccur tcurr
  if {!$sccur || $tcurr eq {}} {return no}
  set tabs [my $BID listFlag]
  if {[set i [my Aux_IndexInList $tcurr $tabs]]==-1} {return no}
  incr i $dir
  set TID [lindex $tabs $i 0]
  if {[lindex $tabs $i 2] eq "1" && ![my Disabled $TID]} {
    my $TID Tab_BeCurrent  ;# TID visible & enabled
    return yes
  }
  return no
}
#_______________________

method ArrowsState {tleft tright sright} {
# Sets a state of scrolling arrows.
#   tleft, tright - index of left/right tab
#   sright - state of a right arrow ("no" for disabled)

  lassign [my [set BID [my ID]] cget -WWID -hidearrows -tiplen] wwid hidearr tiplen
  lassign $wwid wframe wlarr wrarr
  set tabs [my $BID listTab]
  if {$tleft} {
    if {$hidearr && [catch {pack $wlarr -before $wframe -side left}]} {
      pack $wlarr -side left
    }
    set state normal
  } else {
    if {$hidearr} {
      set state normal
      pack forget $wlarr
    } else {
      catch {pack $wlarr -before $wframe -side left}
      set state disabled
    }
  }
  $wlarr configure -state $state
  set tip {}
  if {$state eq {normal} && $tiplen>=0} {
    for {set i [expr {$tleft-1}]} {$i>=0} {incr i -1} {
      if {$tiplen && [incr cntl]>$tiplen} {
        append tip "..."
        break
      }
      set text [lindex [my Tab_TextEllipsed $BID [lindex $tabs $i 1]] 0]
      append tip "$text\n"
    }
  }
  catch {::baltip::tip $wlarr [string trim $tip]}
  if {$sright} {
    if {$hidearr && [catch {pack $wrarr -after $wframe -side right -anchor e}]} {
      pack $wrarr -side right -anchor e
    }
    set state normal
  } else {
    if {$hidearr} {
      set state normal
      pack forget $wrarr
    } else {
      catch {pack $wrarr -after $wframe -side right -anchor e}
      set state disabled
    }
  }
  $wrarr configure -state $state
  set tip {}
  if {$state eq {normal} && $tiplen>=0} {
    for {set i [expr {$tright+1}]} {$i<[llength $tabs]} {incr i} {
      if {$tiplen && [incr cntr]>$tiplen} {
        append tip ...
        break
      }
      set text [lindex [my Tab_TextEllipsed $BID [lindex $tabs $i 1]] 0]
      append tip "$text\n"
    }
  }
  catch {::baltip::tip $wrarr [string trim $tip]}
}
#_______________________

method FillMenuList {BID popi {TID -1} {mnu ""} {mustBeSorted {}}} {
# Fills "List of tabs" item of popup menu.
#   popi - menu of tab items
#   TID - clicked tab ID
#   mnu - root menu
#   mustBeSorted - flag "sorted list"
# Return a list of items types: s (separator) and TID.

  lassign [my $BID cget -tiplen -popuptip -sortlist -comlist] tiplen popuptip sortlist comlist
  set vis [set seps 0] ;# flags for separators: before/after visible items
  set idx [set icom -1]
  set res [list]
  set tabs [my [set BID [my ID]] listFlag]
  if {$mustBeSorted ne {}} {set sortlist $mustBeSorted}
  if {$sortlist} {
    set tabs [lsort -index 1 -dictionary $tabs]
  }
  foreach tab $tabs {
    incr icom
    lassign $tab tID text vsbl
    if {!$sortlist} {
      if {$vsbl && !$seps || !$vsbl && $vis} {
        incr idx
        $popi add separator
        lappend res s
        incr seps
        set vis 0
      } elseif {$vsbl} {
        set vis 1
      }
      if {!$seps && $vis} { ;# no invisible at left
        incr idx
        $popi add separator
        lappend res s
        incr seps
      }
    }
    set dsbl {}
    if {$TID == -1 || $mnu eq {bartabs_cascade}} {
      if {$comlist eq {}} {
        set comm "[self] $tID show yes"
      } else {
        set tip [my $tID cget -tip]
        set comm [string map [list %ID $tID %i $icom %t $tip] $comlist]
      }
      if {[my Disabled $tID]} {set dsbl {-state disabled}}
    } else {
      set comm "[self] moveSelTab $TID $tID"
    }
    if {[set cbr [expr {$tiplen>0 && [incr ccnt]>$tiplen}]]} {set ccnt 0}
    incr idx
    if {$popuptip ne {}} {
      # make a tip for menu items
      $popuptip $popi $idx $tID
    }
    $popi add command -label $text -command $comm {*}$dsbl -columnbreak $cbr
    lappend res $tID
  }
  if {$seps<2 && !$sortlist} { ;# no invisible at right
    $popi add separator
    lappend res s
  }
  return $res
}
#_______________________

method Width {} {
# Calculates and returns the bar width to place tabs.

  lassign [my [set BID [my ID]] cget \
    -tleft -tright -LLEN -wbase -wbar -ARRLEN -hidearrows -WWID -BWIDTH -wproc] \
    tleft tright llen wbase wb arrlen hidearrows wwid bwidth1 wproc
  set iarr 2
  if {$hidearrows} {  ;# how many arrows are visible?
    if {!$tleft} {incr iarr -1}
    if {$tright==($llen-1)} {incr iarr -1}
  }
  set minus2len [expr {-$iarr*$arrlen}]
  set bwidth2 0
  if {$wproc ne {}} {
    set bwidth2 [{*}[string map [list %b $BID] $wproc]]
  }
  if {$bwidth2<2 && [set wbase_exist [winfo exists $wbase]]} {
    # 'wbase' is a base widget to get the bartabs' width from
    set bwidth2 [my Aux_WidgetWidth $wbase]
  }
  incr bwidth2 $minus2len
  set wbase_exist [expr {$bwidth2>1}]
  if {$wbase_exist} {
    set bwidth $bwidth2
  } else {
    if {$bwidth1 eq {} || $bwidth1<=1} {set bwidth1 100}
    set bwidth [expr {$wbase_exist ? min($bwidth1,$bwidth2) : $bwidth1}]
  }
  if {[set winw [winfo width .]]<2} {set winw [winfo reqwidth .]}
  incr winw $minus2len
  if {$bwidth<=0} { ;# last refuge
    set bwidth [expr {max($winw,[winfo reqwidth $wb],[winfo width $wb])}]
  } elseif {$wbase eq {} && $bwidth1 && $winw>1 && $bwidth1>$winw} {
    set bwidth $winw
  }
  return $bwidth
}
#_______________________

method FillFromLeft {{ileft ""} {tright "end"}} {
# Fills a bar with tabs from the left to the right (as much tabs as possible).
#   ileft - index of a left tab
#   tright - index of a right tab

  lassign [my Aux_InitDraw [set BID [my ID]]] bwidth vislen bd arrlen llen tleft hidearr tabs wframe
  if {$ileft ne {}} {set tleft $ileft}
  for {set i $tleft} {$i<$llen} {incr i} {
    lassign [my Tab_DictItem [lindex $tabs $i]] TID text wb wb1 wb2 pf
    lassign [my Tab_Create $BID $TID $wframe $text] wb wb1 wb2
    if {[my Aux_CheckTabVisible $wb $wb1 $wb2 $i $tleft tright vislen \
    $llen $hidearr $arrlen $bd $bwidth tabs $TID $text]} {
      my Tab_Pack $BID $TID $wb $wb1 $wb2
    }
  }
  my Aux_EndDraw $BID $tleft $tright $llen
}
#_______________________

method FillFromRight {tleft tright behind} {
# Fills a bar with tabs from the right to the left (as much tabs as possible).
#   tleft, tright - index of left/right tab
#   behind - flag "go behind the right tab"

  set llen [my [set BID [my ID]] cget -LLEN]
  if {$tright eq "end" || $tright>=$llen} {set tright [expr {$llen-1}]}
  my $BID configure -tleft $tright -tright $tright
  lassign [my Aux_InitDraw $BID] bwidth vislen bd arrlen llen tleft hidearr tabs wframe
  set totlen 0
  for {set i $tright} {$i>=0} {incr i -1} {
    lassign [my Tab_DictItem [lindex $tabs $i]] TID text wb wb1 wb2 pf
    lassign [my Tab_Create $BID $TID $wframe $text] wb wb1 wb2
    incr vislen [set wlen [my $TID cget -width]]
    if {$i<$tright && ($vislen+($tright<($llen-1)||!$hidearr?$arrlen:0))>$bwidth} {
      set pf {}
    } else {
      set tleft $i
      set pf p
      incr totlen $wlen
    }
    set tabs [lreplace $tabs $i $i [my Tab_ItemDict $TID $text $wb $wb1 $wb2 $pf]]
  }
  set i $tright
  while {$behind && [incr i]<$llen && $totlen<$bwidth} {
    # go behind the right tab as far as possible
    lassign [my Tab_DictItem [lindex $tabs $i]] TID text wb wb1 wb2 pf
    lassign [my Tab_Create $BID $TID $wframe $text] wb wb1 wb2
    incr totlen [my $TID cget -width]
    if {($totlen+($i<($llen-1)||!$hidearr?$arrlen:0))>$bwidth} {
      set pf {}
    } else {
      set tright $i
      set pf p
    }
    set tabs [lreplace $tabs $i $i [my Tab_ItemDict $TID $text $wb $wb1 $wb2 $pf]]
  }
  for {set i $tleft} {$i<$llen} {incr i} {
    lassign [my Tab_DictItem [lindex $tabs $i]] TID text wb wb1 wb2 pf
    if {[my Tab_Is $wb] && $pf ne {}} {my Tab_Pack $BID $TID $wb $wb1 $wb2}
  }
  my Aux_EndDraw $BID $tleft $tright $llen
}
#_______________________

method Locked {BID} {
# Checks for "draw locked" mode: protects the menu.

  expr {[my $BID cget -LOCKDRAW] ne {}}
}
#_______________________

method Refill {itab left {behind false}} {
# Fills a bar with tabs.
#   itab - index of tab
#   left - if "yes", fill from left to right
#   behind - flag "go behind the right tab"

  if {[my Locked [set BID [my ID]]]} return
  my $BID clear
  if {$itab eq "end" || $itab==([my $BID cget -LLEN]-1)} {set left 0}
  if {$left} {
    my $BID FillFromLeft $itab
  } else {
    my $BID FillFromRight 0 $itab $behind
  }
}
#_______________________

method CheckDsblPopup {BID TID mnuit} {
# Controls disabling of Close* menu items.
#   mnuit - menu label
# Returns "yes" for disabled menu item

  lassign [my Tab_BID $TID] BID icur
  lassign [my $BID cget -static -LLEN] static llen
  set dsbl [my Disabled $TID]
  lassign [my Mc_MenuItems] list behind close closeall closeleft closeright
  switch -exact -- $mnuit [list \
    BHND {
      if {$static} {return 2}
      if {[set slen [llength [my $BID listFlag "s"]]]>1} {
        set mnuit [string map [list %n $slen] [msgcat::mc "%n tabs"]]
      } else {
        lassign [my Tab_TextEllipsed $BID [my $TID cget -text] 16] mnuit
        set mnuit "\"$mnuit\""
      }
      return [list [expr {$dsbl||$llen<2||$llen==2&&$icur==1}] {} "$mnuit $behind"]
    } \
    $close - $closeall - {} {
      if {$static} {return 2}
    } \
    $closeleft {
      if {$static} {return 2}
      return [expr {$dsbl || !$icur}]
    } \
    $closeright {
      if {$static} {return 2}
      return [expr {$dsbl || $icur==($llen-1)}]
    } \
  ]
  return $dsbl
}
#_______________________

method NeedDraw {} {
# Redraws a bar at need.

  set BID [my ID]
  lassign [my $BID cget -wproc -BWIDTH -ARRLEN] wproc bwo arrlen
  set bw [{*}[string map [list %b $BID] $wproc]]
  if {$bwo eq {} || [set need [expr {abs($bwo-$bw)>$arrlen} && $bw>10]]} {
    my $BID configure -BWIDTH $bw
  }
  if {$bwo ne {} && $need} {
    catch {after cancel $::bartabs::NewAfterID($BID)}
    set ::bartabs::NewAfterID($BID) [after 10 [list [self] $BID draw]]
  }
}

## ____________ Exported methods of Bar ____________ ##

method _runBound_ {w ev args} {
# Runs a method bound to an event occuring at a widget.
#   w - widget
#   ev - event
#   args - the bound method & its arguments

  if {[catch {my {*}$args}]} { ;# failed binding => remove it
    foreach b [split [bind $w $ev] \n] {
      if {[string first $args $b]==-1} {
        if {[incr is1]==1} {bind $w $ev $b} {my bindToEvent $w $ev $b}
      }
    }
  }
}

export _runBound_

## ____________ Auxiliary methods of Bar ____________ ##

method Aux_WidgetWidth {w} {
# Calculates a widget's width.

  if {![winfo exists $w]} {return 0}
  set wwidth [winfo width $w]
  if {$wwidth<2} {set wwidth [winfo reqwidth $w]}
  return $wwidth
}
#_______________________

method Aux_InitDraw {BID {clearpf yes}} {
# Auxiliary method used before cycles drawing tabs.
# Returns a list of main options of drawing tabs.

  my $BID InitColors
  lassign [my $BID cget \
    -tleft -hidearrows -LLEN -WWID -bd -wbase -wbar -ARRLEN -wproc] \
    tleft hidearr llen wwid bd wbase wbar arrlen wproc
  lassign $wwid wframe wlarr
  if {$arrlen eq {}} {
    set arrlen [winfo reqwidth $wlarr]
    my $BID configure -wbase $wbase -ARRLEN $arrlen
  }
  set bwidth [my $BID Width]
  set vislen [expr {$tleft || !$hidearr ? $arrlen : 0}]
  set tabs [my $BID cget -TABS]
  if {$clearpf} {foreach tab $tabs {my [lindex $tab 0] configure -pf {}}}
  list $bwidth $vislen $bd $arrlen $llen $tleft $hidearr $tabs $wframe
}
#_______________________

method Aux_CheckTabVisible {wb wb1 wb2 i tleft trightN vislenN llen hidearr arrlen bd bwidth tabsN TID text} {
# Auxiliary method used to check if a tab is visible.

  upvar 1 $trightN tright $tabsN tabs $vislenN vislen
  incr vislen [my $TID cget -width]
  if {$i>$tleft && ($vislen+(($i+1)<$llen||!$hidearr?$arrlen:0))>$bwidth} {
    pack forget $wb
    set pf {}
  } else {
    set tright $i
    set pf p
  }
  my $TID configure -wb $wb -wb1 $wb1 -wb2 $wb2 -pf $pf
  string length $pf
}
#_______________________

method Aux_EndDraw {BID tleft tright llen} {
# Auxiliary method used after cycles drawing tabs.

  my $BID ArrowsState $tleft $tright [expr {$tright < ($llen-1)}]
  my $BID configure -tleft $tleft -tright $tright
  my Tab_Bindings $BID
  my Tab_MarkBar $BID
}
#_______________________

method Aux_IndexInList {ID lst} {
# Searches ID in list.

  set i 0
  foreach it $lst {
    if {[lindex $it 0]==$ID} {return $i}
    incr i
  }
  return -1
}

## ____________ Public methods of Bar ____________ ##

method cget {args} {
# Gets values of options of bars & tabs.
#   args - list of options, e.g. {-tabcurrent -MyOpt}
# Return a list of values or one value if args is one option.

  set BID [my ID]
  variable btData
  set res [list]
  set llen [dict get $btData $BID -LLEN]
  foreach opt $args {
    if {$opt eq "-listlen"} {
      lappend res $llen
    } elseif {$opt eq "-width"} {
      lassign [dict get [dict get $btData $BID] -wbar] wbar
      lappend res [my Aux_WidgetWidth $wbar]
    } elseif {[dict exists $btData $BID $opt] && ($llen || $opt ne "-tabcurrent")} {
      lappend res [dict get $btData $BID $opt]
    } else {
      lappend res {}
    }
  }
  if {[llength $args]==1} {return [lindex $res 0]}
  return $res
}
#_______________________

method configure {args} {
# Sets values of options for bars & tabs.
#   args - list of pairs "option value"

  set BID [my ID]
  variable btData
  foreach {opt val} $args {
    dict set btData $BID $opt $val
    if {$opt eq "-TABS"} {dict set btData $BID -LLEN [llength $val]}
  }
  if {[dict exists $args -static]} {my $BID Style}
}
#_______________________

method draw {{upd yes}} {
# Draws the bar tabs at slight changes.
#   upd - if "yes", run "update" before redrawing

  if {[my Locked [set BID [my ID]]]} return
  if {$upd} update
  lassign [my Aux_InitDraw $BID] bwidth vislen bd arrlen llen tleft hidearr tabs wframe
  set tright [expr {$llen-1}]
  for {set i $tleft} {$i<$llen} {incr i} {
    lassign [my Tab_DictItem [lindex $tabs $i]] TID text wb wb1 wb2 pf
    lassign [my Tab_Create $BID $TID $wframe $text] wb wb1 wb2
    if {[my Aux_CheckTabVisible $wb $wb1 $wb2 $i $tleft tright vislen $llen $hidearr $arrlen $bd $bwidth tabs $TID $text]} {
      my Tab_Pack $BID $TID $wb $wb1 $wb2
    }
  }
  my Aux_EndDraw $BID $tleft $tright $llen
  my Tab_MarkBar $BID
}
#_______________________

method update {} {
# Updates the bar in hard way.

  if {[my Locked [set BID [my ID]]]} return
  update
  my $BID Refill 0 yes
}
#_______________________

method clear {} {
# Forgets (hides) the shown tabs.

  if {[my Locked [set BID [my ID]]]} return
  set wlist []
  foreach tab [my $BID listTab] {
    lassign $tab TID text wb wb1 wb2 pf
    if {[my Tab_Is $wb] && $pf ne {}} {
      lappend wlist $wb
      my $TID configure -pf {}
    }
  }
  if {[llength $wlist]} {pack forget {*}$wlist}
}
#_______________________

method scrollLeft {} {
  # Scrolls tabs to the left.

  set BID [my ID]
  lassign [my $BID cget -wbar -dotip] w dotip
  set wlarr $w.larr   ;# left arrow
  if {[my $BID ScrollCurr -1]} {
    if {$dotip} {catch {::baltip::repaint $wlarr}}
    return
  }
  lassign [my $BID cget -tleft -LLEN -scrollsel] tleft llen sccur
  if {![string is integer -strict $tleft]} {set tleft 0}
  if {$tleft && $tleft<$llen} {
    incr tleft -1
    set tID [lindex [my $BID listTab] $tleft 0]
    my $BID configure -tleft $tleft
    my $BID Refill $tleft yes
    if {$sccur} {my $tID Tab_BeCurrent}
    if {$dotip} {catch {::baltip::repaint $wlarr}}
  }
}
#_______________________

method scrollRight {} {
  # Scrolls tabs to the right.

  set BID [my ID]
  lassign [my $BID cget -wbar -dotip] w dotip
  set wrarr $w.rarr   ;# left arrow
  if {[my $BID ScrollCurr 1]} {
    if {$dotip} {catch {::baltip::repaint $wrarr}}
    return
  }
  lassign [my $BID cget -tright -LLEN -scrollsel] tright llen sccur
  if {![string is integer -strict $tright]} {set tright [expr {$llen-2}]}
  if {$tright<($llen-1)} {
    incr tright
    set tID [lindex [my $BID listTab] $tright 0]
    my $BID configure -tright $tright
    my $BID Refill $tright no
    if {$sccur} {my $tID Tab_BeCurrent}
    if {$dotip} {catch {::baltip::repaint $wrarr}}
  }
}
#_______________________

method listTab {} {
# Gets a list of tabs.
# Returns a list of TID, text, wb, wb1, wb2, pf.

  set res [list]
  foreach tab [my [my ID] cget -TABS] {lappend res [my Tab_DictItem $tab]}
  return $res
}
#_______________________

method comparetext {it1 it2} {
# Compares items (by -text attribute) for sort method.
#   it1 - 1st item to compare
#   it2 - 2nd item to compare
# See also: sort

  catch {set it1 [dict get $it1 -text]}
  catch {set it2 [dict get $it2 -text]}
  string compare -nocase $it1 $it2
}
#_______________________

method sort {{mode -increasing} {cmd ""}} {
# Sorts a list of tabs by the tab names.
#   mode - option of sort
#   cmd - command to compare two items

  set BID [my ID]
  lassign [my $BID cget -tabcurrent -lifo] TID lifo
  set tabs [my $BID cget -TABS]
  if {$cmd eq {}} {
    set tabs [lsort $mode -index 1 -dictionary -command "[self] comparetext" $tabs]
  } else {
    set tabs [lsort $mode -dictionary -command $cmd $tabs]
  }
  my $BID configure -TABS $tabs -lifo no
  my $TID show yes
  my $BID configure -lifo $lifo
}
#_______________________

method listFlag {{filter ""}} {
# Gets a list of TID + flags "visible", "marked", "selected", "disabled".
#   filter - "" for all or "v","m","s","d" for visible, marked, selected, disabled
# Returns a list "TID, text, visible, marked, selected, disabled" for all or a list of TID for filtered.

  set BID [my ID]
  lassign [my $BID cget -mark -disable -select -tabcurrent] mark dsbl fewsel tcurr
  set res [list]
  foreach tab [my $BID listTab] {
    lassign $tab TID text wb wb1 wb2 pf
    set visibl [expr {[my Tab_Is $wb] && $pf ne {}}]
    set marked [expr {[lsearch $mark $TID]>=0}]
    set dsbled [expr {[lsearch $dsbl $TID]>=0}]
    set select [expr {$TID == $tcurr || [lsearch $fewsel $TID]>-1}]
    if {$filter eq {}} {
      lappend res [list $TID $text $visibl $marked $select $dsbled]
    } elseif {$filter eq "v" && $visibl || $filter eq "m" && $marked || \
              $filter eq "d" && $dsbled || $filter eq "s" && $select} {
      lappend res $TID
    }
  }
  return $res
}
#_______________________

method insertTab {txt {pos "end"} {img ""}} {
# Inserts a new tab into a bar.
#   txt - tab's label
#   pos - tab's position in tab list
#   img - tab's image
# Returns TID of new tab or "".

  set tabs [my [set BID [my ID]] cget -TABS]
  set tab [my Tab_Data $BID $txt]
  if {$tab eq {}} {return {}}
  if {$pos eq {end}} {
    lappend tabs $tab
  } else {
    set tabs [linsert $tabs $pos $tab]
  }
  if {$img ne {}} {
    set imagetabs [my $BID cget -IMAGETABS]
    lappend imagetabs [list [lindex $tab 0] $img]
    my $BID configure -IMAGETABS $imagetabs
  }
  my $BID configure -TABS $tabs
  my $BID Refill $pos [expr {$pos ne "end"}]
  lindex $tab 0
}
#_______________________

method tabID {txt} {
# Gets TID by tab's label.
#   txt - label
# Returns TID or -1.

  set BID [my ID]
  if {[catch {set ellipse [my $BID cget -ELLIPSE]}]} {return {}}
  if {[string first $ellipse $txt]>0} {
    set pattern [string map [list $ellipse "*"] $txt]
  } else {
    set pattern {}
  }
  foreach tab [my $BID listTab] {
    lassign $tab tID ttxt
    if {$txt eq $ttxt} {return $tID}
    if {$pattern ne {} && [string match $pattern $ttxt]} {return $tID}
  }
  return {}
}
#_______________________

method popList {{X ""} {Y ""} {sortedList 0}} {
# Shows a menu of tabs.
#   X - x coordinate of mouse pointer
#   Y - y coordinate of mouse pointer
#   sortedList - flag "sorted list"

  set BID [my ID]
  my $BID DestroyMoveWindow
  lassign [my $BID cget -wbar -title] wbar title
  set popi $wbar.popupList
  catch {destroy $popi}
  menu $popi -tearoff 1 -title $title
  if {[set plist [my $BID FillMenuList $BID $popi -1 {} $sortedList]] eq "s"} {
    destroy $popi
  } else {
    my Bar_MenuList $BID -1 $popi $plist
    if {$X eq {}} {lassign [winfo pointerxy .] X Y}
    tk_popup $popi $X $Y
  }
}
#_______________________

method remove {} {
# Removes a bar.
# Returns "yes" at success.

  set BID [my ID]
  variable btData
  if {[dict exists $btData $BID]} {
    catch {bind [my $BID cget -wbase] <Configure> {}}
    lassign [my $BID cget -BINDWBASE] wb bnd
    if {$wb ne {}} {bind $wb <Configure> $bnd}
    set bar [dict get $btData $BID]
    foreach tab [dict get $bar -TABS] {my Tab_RemoveLinks $BID [lindex $tab 0]}
    catch {destroy {*}[dict get $bar -WWID]}
    catch {destroy [my $BID cget -UNDERWID]}
    if {[set bc [my $BID cget -BARCOM]] ne {}} {catch {rename $bc {}}}
    foreach tc [my $BID cget -TABCOM] {catch {rename [lindex $tc 1] {}}}
    dict unset btData $BID
    return yes
  }
  return no
}
#_______________________

method moveTab {TID pos} {
  # Moves a tab to a new position in the bar.
  #   pos - the new position

  set BID [my ID]
  set tabs [my $BID cget -TABS]
  if {[set i [lsearch -index 0 $tabs $TID]]>-1} {
    set tab [lindex $tabs $i]
    set tabs [lreplace $tabs $i $i]
    my $BID configure -TABS [linsert $tabs $pos $tab]
  }
}
#_______________________

method checkDisabledMenu {BID TID func} {
# Checks whether the popup menu's items are disabled.
#   func - close function
# *func* equals to:
#   1 - for "Close All"
#   2 - for "Close All at Left"
#   3 - for "Close All at Right"
# Returns "yes" if the menu's item is disabled.

  lassign [my Mc_MenuItems] list behind close closeall closeleft closeright
  switch $func {
    1 {set item $closeall}
    2 {set item $closeleft}
    3 {set item $closeright}
    default {set item $close}
  }
  my CheckDsblPopup $BID $TID $item
}
#_______________________

method closeAll {BID TID func args} {
# Closes tabs of bar.
#   func - close function
# *func* equals to:
#   1 - for "Close All"
#   2 - for "Close All at Left"
#   3 - for "Close All at Right"

  switch $func {
    1 {my $BID Tab_CloseFew -1   no {*}$args}
    2 {my $BID Tab_CloseFew $TID yes}
    3 {my $BID Tab_CloseFew $TID no}
  }
}
#_______________________

method bindToEvent {w event args} {
  # Binds an event on a widget to a command.
  #   w - the widget's path
  #   event - the event
  #   args - the command

  if {[string first $args [bind $w $event]]<0} {
    bind $w $event [list + {*}$args]
  }
}

## ____________ EOC Bar ____________ ##

}

# ________________________ Bars _________________________ #

## ____________ Methods of Bars ____________ ##

oo::define ::bartabs::Bars {

variable btData

constructor {args} {
  set btData [dict create]
  if {[llength [self next]]} { next {*}$args }
  oo::objdefine [self] "method tab-1 {args} {return {-1}}"
  lappend ::bartabs::BarsList [self]
}

destructor {
  my removeAll
  unset btData
  set i [lsearch -exact $::bartabs::BarsList [self]]
  set ::bartabs::BarsList [lreplace $::bartabs::BarsList $i $i]
  if {[llength [self next]]} next
}

## ____________ Private methods of Bars ____________ ##

method Bars_Method {mtd args} {
# Executes a method for all bars.
#   mtd - method's name
#   args - method's arguments

  foreach BID [lsort -decreasing [dict keys $btData]] {my $BID $mtd {*}$args}
}
#_______________________

method MarkTab {opt args} {
# Sets option of tab(s).
#   opt - option
#   args - list of TID

  foreach TID $args {
    if {$TID ni {{} -1}} {
      set BID [lindex [my Tab_BID $TID] 0]
      set marktabs [my $BID cget $opt]
      if {[lsearch $marktabs $TID]<0} {
        lappend marktabs $TID
        my $BID configure $opt $marktabs
      }
    }
  }
  my Tab_MarkBars
}
#_______________________

method UnmarkTab {opt args} {
# Unsets option of tab(s).
#   opt - option
#   args - list of TID

  if {![llength $args]} {my Bars_Method configure $opt [list]}
  foreach TID $args {
    if {$TID ni {{} -1}} {
      set BID [lindex [my Tab_BID $TID] 0]
      set marktabs [my $BID cget $opt]
      if {[set i [lsearch $marktabs $TID]]>=0} {
        my $BID configure $opt [lreplace $marktabs $i $i]
      }
    }
  }
  my Tab_MarkBars
}
#_______________________

method TtkTheme {} {
  # Checks if a standard ttk theme is used.

  expr {[ttk::style theme use] in {clam alt classic default awdark awlight}}
}

## ____________ Public methods of Bars ____________ ##

method create {barCom {barOpts ""} {tab1 ""}} {
# Creates a bar.
#   barCom - bar command's name or barOpts
#   barOpts - list of bar's options
#   tab1 - tab to show after creating the bar
# Returns BID.

  if {[set noComm [expr {$barOpts eq {}}]]} {set barOpts $barCom}
  set w [dict get $barOpts -wbar] ;# parent window
  set wframe $w.frame ;# frame
  set wlarr $w.larr   ;# left arrow
  set wrarr $w.rarr   ;# right arrow
  lappend barOpts -WWID [list $wframe $wlarr $wrarr]
  my My [set BID [my Bar_Data $barOpts]]
  my $BID InitColors
  set bgm [my $BID cget -BGMAIN]
  if {[my TtkTheme]} {
    ttk::button $wlarr -style ClButton$BID -image bts_ImgLeft \
      -command [list [self] $BID scrollLeft] -takefocus 0
    ttk::button $wrarr -style ClButton$BID -image bts_ImgRight \
      -command [list [self] $BID scrollRight] -takefocus 0
  } else {
    button $wlarr -image bts_ImgLeft -borderwidth 0 -highlightthickness 0 \
      -command [list [self] $BID scrollLeft] -takefocus 0 -background $bgm
    button $wrarr -image bts_ImgRight -borderwidth 0 -highlightthickness 0 \
      -command [list [self] $BID scrollRight] -takefocus 0 -background $bgm
  }
  if {$bgm eq {}} {set style {}} {set style "-background $bgm"}
  frame $wframe -relief flat {*}$style
  pack $wlarr -side left -padx 0 -pady 0 -anchor e
  pack $wframe -after $wlarr -side left -padx 0 -pady 0 -fill x -expand 1
  pack $wrarr -after $wframe -side right -padx 0 -pady 0 -anchor w
  if {[my $BID cget -separator]} {
    if {![winfo exists $w.under]} {
      ttk::separator $w.under -orient horizontal
      my $BID configure -UNDERWID $w.under
    }
    pack $w.under -before $wlarr -side bottom -fill x -expand 1 -padx 0 -pady 2
  }
  foreach w {wlarr wrarr} {
    bind [set $w] <Button-3> "[self] $BID popList %X %Y"
  }
  set wbase [my $BID cget -wbase]
  if {$wbase ne {}} {
    after 1 [list \
      my $BID configure -BINDWBASE [list $wbase [bind $wbase <Configure>]] ; \
      my $BID bindToEvent $wbase <Configure> [self] _runBound_ $wbase <Configure> $BID NeedDraw]
  }
  if {!$noComm} {
  ; proc $barCom {args} "return \[[self] $BID {*}\$args\]"
    my $BID configure -BARCOM $barCom
  }
  if {$tab1 eq {}} {
    after 50 [list [self] $BID NeedDraw ; [self] $BID draw]
  } else {
    set tab1 [my $BID tabID $tab1]
    if {$tab1 ne {}} {after 100 "[self] $BID clear; [self] $BID $tab1 show yes"}
  }
  return $BID
}
#_______________________

method updateAll {} {
# Updates all bars in hard way.

  my Bars_Method Refill 0 yes
}
#_______________________

method drawAll {{upd yes}} {
# Redraws all bars.
#   upd - if "yes", run "update" before redrawing

  if {$upd} update
  my Bars_Method draw no
}
#_______________________

method removeAll {} {
# Removes all bars.

  my Bars_Method remove
}
#_______________________

method markTab {args} {
# Marks tab(s).
#   args - list of TID

  my MarkTab -mark {*}$args
}
#_______________________

method unmarkTab {args} {
# Unmarks tab(s).
#   args - list of TID or {}

  my UnmarkTab -mark {*}$args
}
#_______________________

method onSelectCmd {args} {
  # Runs a command (set by "-csel3") on a list of tabs.
  #   args - list of TID

  foreach TID $args {
    if {$TID ni {{} -1}} {
      set BID [lindex [my Tab_BID $TID] 0]
      my $BID Bar_Cmd2 -csel3 $TID ;# command after the action
    }
  }
}
#_______________________

method selectTab {args} {
# Selects tab(s).
#   args - list of TID

  my MarkTab -select {*}$args
  my onSelectCmd {*}$args
}
#_______________________

method unselectTab {args} {
# Unselects tab(s).
#   args - list of TID or {}

  my UnmarkTab -select {*}$args
  my onSelectCmd {*}$args
}
#_______________________

method enableTab {args} {
# Enables tab(s).
#   args - list of TID or {}

  my UnmarkTab -disable {*}$args
}
#_______________________

method disableTab {args} {
# Disables tab(s).
#   args - list of TID

  my MarkTab -disable {*}$args
}
#_______________________

method isTab {TID} {
# Checks if a tab exists.
#   TID - tab ID
# Returns true if the tab exists.

  expr {[my Tab_BID $TID check] ne {}}
}
#_______________________

method MoveTab {TID1 TID2} {
# Changes a tab's position in bar.
#   TID1 - TID of the moved tab
#   TID2 - TID of a tab to move TID1 behind
# TID1 and TID2 must be of the same bar.

  lassign [my Tab_BID $TID1] BID1 i1
  lassign [my Tab_BID $TID2] BID2 i2
  if {$i1!=$i2 && $BID1 eq $BID2} {
    set tabs [my $BID1 cget -TABS]
    set tab [lindex $tabs $i1]
    set tabs [lreplace $tabs $i1 $i1]
    set i [expr {$i1>$i2?($i2+1):$i2}]
    my $BID1 configure -TABS [linsert $tabs $i $tab]
    # let a neighbor be shown if possible
    set TID [lindex $tabs $i1 0]
    if {[catch {my $TID show yes}]} {
      my $TID1 show yes ;# old way: show 1st moved tab
    }
  }
}

#_______________________

method moveSelTab {TID1 TID2} {
# Changes a tab's or selected tabs' position in bar.
#   TID1 - TID of the moved tab
#   TID2 - TID of a tab to move TID1 behind
# TID1 and TID2 must be of the same bar.

  if {[my $TID1 Tab_Cmd -cmov] ni {"1" "yes" "true"}} return ;# chosen to not move
  set BID [my Tab_BID $TID1 check]
  # -lifo option prevents moving, so it has to be temporarily disabled
  set lifo [my $BID cget -lifo]
  my $BID configure -lifo no
  set seltabs [my $BID listFlag "s"]
  if {[set i [llength $seltabs]]>1} {
    for {incr i -1} {$i>=0} {incr i -1} {
      set tid [lindex $seltabs $i]
      if {$tid ne $TID2} {my MoveTab $tid $TID2}
    }
    my $BID Bar_Cmd2 -cmov3
  } else {
    my MoveTab $TID1 $TID2
    my $BID Bar_Cmd2 -cmov3 $TID1 ;# command after the action
  }
  my $BID configure -lifo $lifo  ;# restore -lifo option
}

## ____________ EOC Bars ____________ ##

}

# ________________________________ EOF __________________________________ #

bartabs.tcl