# Hidetabs plugin for Tkabber.
#
# Authors: 
#    rejjin <xmpp:rejjin@jabber.dk>
#    ancestor <xmpp:ancestor@jabber.org>

namespace eval hidetabs {
	variable self
	variable config
	
	set cdir [file dirname [info script]]
	
	package require msgcat
	::msgcat::mcload [file join $cdir msgs]
	
	namespace import ::msgcat::mc
	
	if { ! $::ifacetk::options(use_tabbar)} {
		hook::add finload_hook {
			tk_messageBox -message [mc "Plugin \"hidetabs\"\
			can't load. Please enable tabs."] -title  [mc "Conflict"]
		}
		return
	}
	
	set self(fr) .nb
	set self(state) 1
	set self(menu-fr) .menu_tabbar
	set self(tabsmenu) .tabsmenu
	
	set config(nonecolor) ""
	set config(unread_msg_color) ""
	set config(personal_msg_color) ""
	set config(auto_disable_label) 1
	
	set config(key_nb_toggle) "<Key-F5>"
	set config(key_menu_toggle) "<KeyPress-Alt_R>"
	
	custom::defgroup Plugins [mc "Plugins options."] -group Tkabber	
	custom::defgroup hidetabs [mc "Hidetabs plugin options."] -group Plugins	
	
	custom::defvar config(alpha) 90 \
		[mc "Alpha value."] \
		-group hidetabs -type integer
		
	custom::defvar config(side) square \
		[mc "Window construction."] \
		-type options -group hidetabs \
		-values [list \
			vertical [mc "Vertical"] \
			horisontal [mc "Horisontal"] \
			square [mc "Square"]]
	
	custom::defvar config(bwidth) 120 \
		[mc "Button width."] \
		-group hidetabs -type integer
		
	custom::defvar config(bheight) 15 \
		[mc "Button height."] \
		-group hidetabs -type integer

	custom::defvar config(save_last_focus) 1 \
		[mc "Save last focus on button."] \
		-group hidetabs -type boolean

	custom::defvar config(move_window) 0 \
		[mc "Window moving."] \
		-group hidetabs -type boolean

	custom::defvar config(topmost) 1 \
		[mc "Topmost window."] \
		-group hidetabs -type boolean
	
	custom::defvar config(show_unread) 1 \
		[mc "Show number of messages in toolbar."] \
		-group hidetabs -type boolean \
		-command [namespace current]::show_unread
		
	custom::defvar config(show_current_tab) 1 \
		[mc "Show title of active tab."] \
		-group hidetabs -type boolean \
		-command [namespace current]::show_ctab
		
	custom::defvar config(hide_ctab_unread) 0 \
		[mc "Hide labels when tabbar hidden."] \
		-group hidetabs -type boolean \
		-command [namespace current]::hide_labels_process
		
	custom::defvar config(key_nb_toggle) "<Key-F5>" \
		[mc "Key for toggle tabbar."] \
		-group hidetabs -type string \
		-command [namespace current]::change_bindkey

	custom::defvar config(key_menu_toggle) "<KeyPress-Alt_R>" \
		[mc "Key for toggle tabs menu."] \
		-group hidetabs -type string \
		-command [namespace current]::change_bindkey
	
	image create photo hidetabs/none-type \
		-file [file join $cdir pixmaps none-type.gif]

	hook::add finload_hook [namespace current]::load
	hook::add systray_menu_hook [namespace current]::systray_menu 20
}

proc hidetabs::systray_menu { m } {
	variable self
	$m configure -postcommand [list [namespace current]::generate_menu $m]
	$m insert end command -label [mc "Dismiss all tabs"] \
		-command [namespace current]::dismiss_all
	menu $m.hidetabs -title [mc "Tabs"] -tearoff 0 \
		-postcommand [list [namespace current]::pcm_menu $m.hidetabs]
	$m add cascade -label [mc "Tabs"] -menu $m.hidetabs
	$m add separator
}

proc hidetabs::hide_labels_process {args} {
	variable config
	variable self
	
	if { $self(state) && $config(hide_ctab_unread)} { 
		delete_unread
		delete_ctab
	}
}

proc hidetabs::generate_menu { m } {
	variable self
	
	if { [info exist self(idx-menu)] && $self(idx-menu) > 0} {
	$m delete 0 $self(idx-menu)
	}
	
	set self(idx-menu) 0
	foreach ind [get_tabs_data] {
		lassign $ind tab color tabtitle number_msg personal_msg cmd icon c_tab

		if {$number_msg > 0} { 
			append tabtitle " ($number_msg)" 
			if {[string equal $tab $c_tab]} { 
			set tabtitle "\[ $tabtitle \]" 
			}
			$m insert $self(idx-menu) command -label $tabtitle -command $cmd \
			-image $icon -compound left -foreground $color
			incr self(idx-menu)
		}
	}
	if {$self(idx-menu) > 0} {
	incr self(idx-menu)
	$m insert $self(idx-menu) separator
	}
	set l 0
	foreach ind [get_tabs_data] {
	incr l
	}
}

proc hidetabs::load {} {
	variable config
	
	set m [winfo parent [.mainframe getmenu tkabber]]
	$m add cascade -label [mc "Tabs"] -menu $m.list
	menu $m.list -postcommand [list [namespace current]::pcm_menu $m.list] \
		-tearoff 0 -title [mc "Tabs"] 
	
	show_unread
	show_ctab
	hide_labels_process
	
	bind . $config(key_nb_toggle) [namespace current]::toggle_tabbar
	bind . $config(key_menu_toggle) [list [namespace current]::menu_key 0 toggle]
}

# TODO: support plugin manager
proc hidetabs::unload {} {
	variable config
	
	set m [winfo parent [.mainframe getmenu tkabber]]
	if { ![catch { $m index  [mc "Tabs"] }] } {
		$m delete [$m index  [mc "Tabs"]]
	}
	catch {destroy $m.list}
	
	delete_unread
	delete_ctab
	
	bind . $config(key_nb_toggle) {}
	bind . $config(key_menu_toggle) {}
}

proc hidetabs::delete_unread {} {
	set w [.mainframe gettoolbar 0]
	catch { destroy $w.unreadl }
}

proc hidetabs::delete_ctab {} {
	set w [.mainframe gettoolbar 0]
	catch { destroy $w.ctab }
}

proc hidetabs::show_unread { args } {
	variable config
	
	set w [.mainframe gettoolbar 0]
	
	if { !$config(show_unread) } {
		catch { destroy $w.unreadl }
		return
	}
	
	if { [winfo exist $w.unreadl] } {
	destroy $w.unreadl
	}
	
	label $w.unreadl
	
	if { [winfo exist $w.ctab] } {
	pack $w.unreadl -side right -padx 3 -before $w.ctab
	} else {
	pack $w.unreadl -side right -padx 3
	}
	
	if { $config(nonecolor) == "" } {
	set config(nonecolor) [$w.unreadl cget -foreground]
	}
	
	bind $w.unreadl <1> [list [namespace current]::next_unread $w.unreadl]
	bind $w.unreadl <3> [list [namespace current]::menu_key 0 toggle]
	
	unread_label_loop $w
}

proc hidetabs::show_ctab { args } {
	variable config
	
	set w [.mainframe gettoolbar 0]
	
	if { !$config(show_current_tab) } {
		catch { destroy $w.ctab}
		return
	}
	
	if { [winfo exist $w.ctab] } {
	return
	}
	
	label $w.ctab
	
	if { [winfo exist $w.unreadl] } {
	pack $w.ctab -side right -after $w.unreadl -padx 0
	} else {
	pack $w.ctab -side right -padx 0
	}
	
	bind $w.ctab <1> [list [namespace current]::menu_ctab_show %W %X %Y]
	bind $w.ctab <Any-Enter> [list [namespace current]::scroll init]
	bind $w.ctab <Any-Leave> [list [namespace current]::scroll destroy]
	
	unread_label_loop $w
}

proc hidetabs::scroll { type } {
	switch -- $type {
		init {
			bind . <<ScrollUp>> [list [namespace current]::open_tab next]
			bind . <<ScrollDown>> [list [namespace current]::open_tab prev]
		}
		destroy {
			bind . <<ScrollUp>> {}
			bind . <<ScrollDown>> {}
		}
	}
}

proc hidetabs::open_tab { type } {
	variable self
	switch -- $type {
		next {
			ifacetk::tab_move $self(fr) +1
		}
		prev {
			ifacetk::tab_move $self(fr) -1
		}
	}
}

proc hidetabs::menu_ctab_show {w x y} {
	set m $w.menu

	if { ![winfo exists $m] } {
		menu $m -tearoff 0
	}

	pcm_menu $m
	
	tk_popup $m $x $y
}

proc hidetabs::next_unread { m } {
	if { [string equal [$m cget -text] "\[ [mc "Messages: %s" _] \]"] } {
		menu_key 0 toggle
		return
	}
	
	foreach chatid [::chat::opened] {
		if {$::ifacetk::personal_msg($chatid) > 0} {
		::chat::activate $chatid
		return
		}
	}
	foreach chatid [::chat::opened] {
		if {$::ifacetk::number_msg($chatid) > 0} {
			::chat::activate $chatid
			return
		}
	}
}

proc hidetabs::unread_label_loop { w } {
	variable after_idle
	
	if { [info exist after_idle] } {
	after cancel $after_idle
	}
	
	if { ![winfo exist $w.unreadl] && ![winfo exist $w.ctab] } {
	return
	}
	
	if { [winfo exist $w.unreadl] } {
	update_unread_label $w
	}
	
	if { [winfo exist $w.ctab] } {
	update_ctab_label $w
	}
	
	set after_idle [after idle "after 100 [list \
	[namespace current]::unread_label_loop $w]"]
}

proc hidetabs::update_unread_label { w } {
	variable config
	
	set unreaded 0
	set color $config(nonecolor)
	set pmsgs 0
	
	foreach chatid [::chat::opened] {
		incr unreaded $::ifacetk::number_msg($chatid)
		if {$::ifacetk::personal_msg($chatid) > 0} {
			incr pmsgs
		}
	}
	
	set ss 0
	if { $unreaded == 0 } {
	set ss 1
	}
	
	if {$unreaded > 0 && $config(unread_msg_color) != ""} {
		set color $config(unread_msg_color)
	} elseif { $unreaded > 0 } {
		set color blue
	}
	
	if {$pmsgs > 0 && $config(personal_msg_color) != ""} {
		set color $config(personal_msg_color)
	} elseif { $pmsgs > 0 } {
		set color red
	}
	
	if { [string equal $unreaded "0"] } {
		set unreaded _
	}
	
	set title [mc "Messages: %s" $unreaded]
	$w.unreadl configure -text "\[ $title \]" -foreground $color -state normal
	
	if { $ss && $config(auto_disable_label) } {
		$w.unreadl configure -state disabled
	}
}

proc hidetabs::update_ctab_label { w } {
	$w.ctab configure -text "" -image ""
	foreach ind [get_tabs_data] {
		lassign $ind tab color tabtitle number_msg personal_msg cmd icon c_tab
		if { [string equal $tab $c_tab] } {
			$w.ctab configure -text "\[ $tabtitle \]" -foreground $color \
			-image $icon -compound left
			bind $w.ctab <3> [list [namespace current]::tab_menu %X %Y $c_tab]
			bind $w.ctab <2> [list ifacetk::destroy_tab $c_tab]
		}
	}
}

proc hidetabs::tab_menu {x y page} {
	variable self
	global curmenutab
	
    set curmenutab $page
	
	set m .hidetabsmenu
	if { ![winfo exist $m]} {
	$self(tabsmenu) clone $m
	$m add separator
	$m add command -label [mc "Dismiss all tabs"] \
	-command [namespace current]::dismiss_all
	}
	
	tk_popup $m $x $y
}

proc hidetabs::dismiss_all {} {
	foreach chatid [::chat::opened] {
		set winid [::chat::winid $chatid]
		set page [ifacetk::nbpage $winid]
		set color [lindex $::alert_colors 0]
		.nb itemconfigure $page \
				-foreground $color \
				-activeforeground $color
		set ::tabcolors($page) 0
		set ::ifacetk::number_msg($chatid) 0
		set ::ifacetk::personal_msg($chatid) 0
		
		ifacetk::update_chat_title $chatid
		ifacetk::update_main_window_title
	}
}


proc hidetabs::change_bindkey {ns key c} {
	variable config
	variable old_key
	
	catch {bind . $old_key($key) {}}
	
	if { [string length $config($key)] == 0 } {
		return
	}

	set old_key($key) $config($key)
	
	bind . $config(key_menu_toggle) \
		[list [namespace current]::menu_key 0 toggle]
	bind . $config(key_nb_toggle) \
		[namespace current]::toggle_tabbar
}

proc hidetabs::pcm_menu { m } {
	$m delete 0 end
	
	foreach ind [get_tabs_data] {
		lassign $ind tab color tabtitle number_msg personal_msg cmd icon c_tab

		if {$number_msg > 0} { append tabtitle " ($number_msg)" }
		
		if {[string equal $tab $c_tab]} { set tabtitle "\[ $tabtitle \]" }
		
		$m add command -label $tabtitle -command $cmd \
			-image $icon -compound left -foreground $color
	}
}


proc hidetabs::toggle_tabbar { args } {
	variable self
	variable config
	
	if { $self(state) } { 
	grid forget $self(fr) 
	show_unread
	show_ctab
	} else { 
	if {$config(hide_ctab_unread)} {
		delete_unread
		delete_ctab
	}
	ifacetk::configure_tabs
	}
	
	set self(state) [expr { ! $self(state) }]
}

proc hidetabs::aset {t var val} {
	set $var -1
	after $t [list set $var $val]
}

proc hidetabs::menu_key { t {toggle ""} } {
	variable S
	variable self
	variable p_fm
	
	if { [info exist S] && $S == -1 } { return }
	set full_s [namespace current]::S
	
	if { [string length $toggle] > 0 } {
		if {[info exist S] && $S} {
			close_menu
			aset 300 $full_s 0
		} else {
			show_menu
			aset 100 $full_s 1
		}
		return
	}
	
	switch $t {
		p {
			show_menu
			aset 100 $full_s 1
		}
		r {
			close_menu
			aset 300 $full_s 0
			array unset p_fm
		}
	}
}

proc hidetabs::show_menu { {r 0} } {
	variable self
	variable config
	variable p_fm
	variable t_current
	
	set process 1
	
	if { ![info exist self(in_r)] || !$self(in_r) } {
		if { $r } {
			return
		}
	}
	
	set self(in_r) 1
	
	if { ![winfo exist $self(menu-fr)]} {
		set process 0
		
		toplevel $self(menu-fr)
		wm state $self(menu-fr) withdraw
		
		bind $self(menu-fr) <Key-Return> [list [namespace current]::invoke_tab %W]
		
		bind $self(menu-fr) <Key-Up> [list [namespace current]::focus_ch up]
		bind $self(menu-fr) <Key-Down> [list [namespace current]::focus_ch down]
		bind $self(menu-fr) <Key-Left> [list [namespace current]::focus_ch left]
		bind $self(menu-fr) <Key-Right> [list [namespace current]::focus_ch right]
		
		bind $self(menu-fr) $config(key_menu_toggle) \
			[list [namespace current]::menu_key r]
		
		if { ! $config(move_window)} { 
			set oldc [bind . <1>]
			bind . <1> [list [namespace current]::menu_key r]
			bind . <1> [list +bind . <1> $oldc]
		}
		
		foreach {option value} [wm attributes $self(menu-fr)] {
			if { [string equal $option -topmost] } {
				wm attributes $self(menu-fr) -topmost $config(topmost)
			}
		}
		
		set alpha [expr $config(alpha) / 100.0]
		if {$alpha <= 1.0} {
			foreach {option value} [wm attributes $self(menu-fr)] {
				if {[string equal $option -alpha]} {
					wm attributes $self(menu-fr) -alpha $alpha
				}
			}
		}
		
		wm title $self(menu-fr) [::msgcat::mc "Tabs"]
		
		if { ! $config(move_window)} {
			catch {wm overrideredirect $self(menu-fr) 1}
		}
		
		wm resizable $self(menu-fr) 0 0
		
		wm protocol $self(menu-fr) WM_DELETE_WINDOW \
			[list [namespace current]::menu_key r]
	}
	
	foreach ind [get_tabs_data] {
		lassign $ind tab color tabtitle number_msg personal_msg cmd icon c_tab

		if {$number_msg > 0} { append tabtitle " ($number_msg)" }
		
		if { ! [winfo exist $self(menu-fr).$tab]} {			
			button $self(menu-fr).$tab -command [list [namespace current]::ncmd_btn $cmd] \
				-foreground black -width $config(bwidth) -anchor w -height $config(bheight) \
				-overrelief sunken
			bind $self(menu-fr).$tab <3> [list [namespace current]::close $tab]
			bind $self(menu-fr).$tab <Key-Delete> [list [namespace current]::close $tab]
		}
		
		if {[string equal $tab $c_tab]} { set tabtitle "\[ $tabtitle \]" }
		
		$self(menu-fr).$tab configure -text $tabtitle -image $icon \
			-compound left -foreground $color
	}
	
	set slv [winfo children $self(menu-fr)]
	if { [llength $slv] <= 1 } {
	return [close_menu]
	}
	
	if { ![info exist t_current] || ![winfo exist $t_current]} {
	set t_current [lindex $slv 0]
	} 	

	if { ! $process} {
		array unset p_fm
		set nb [llength $slv]
		set tm [lindex $slv 0]
		grid $tm
		update idletasks
		set bw [winfo width $tm]
		set bh [winfo height $tm]
		grid forget $tm
		
		if {$nb <= 0} { return }
		
		set it 0
		if { [string equal $config(side) "square"] } {
			set r [expr {round(sqrt($nb))}]
			for {set i 0} {$i < $nb} {incr i $r} {
				incr it
				for {set x 0} {$x < $r} {incr x} {
					set p [lindex $slv [expr $i + $x]]
					if { ! [winfo exist $p]} {continue}
					set c [expr {$i / $r}]
					grid $p -column $c -row $x -sticky news
					set "p_fm($c $x)" $p
				}
			}
		} elseif { [string equal $config(side) "vertical"] } {
			set scv [winfo screenheight .]
			set part [expr {($scv / 100) * 70}]
			set part [expr {$bh * ($part / $bh)}]
			set r 0; set c 0; set lmy 0; set k 1
			foreach p $slv {
				if {$lmy >= [expr {$part * $k}]} {incr c; incr k; set r 0}
				grid $p -column $c -row $r -sticky news -pady 1
				update idletasks
				set "p_fm($c $r)" $p
				incr r; incr lmy [winfo height $p]
			}
		} elseif { [string equal $config(side) "horisontal"] } {
			set scv [winfo screenwidth .]
			set part [expr {($scv / 100) * 70}]
			set part [expr {$bw * ($part / $bw)}]
			set r 0; set c 0; set lmy 0; set k 1
			foreach p $slv {
				if {$lmy >= [expr {$part * $k}]} {incr r; incr k; set c 0}
				grid $p -column $c -row $r -sticky news -padx 1 -pady 1
				update idletasks
				set "p_fm($c $r)" $p
				incr c; incr lmy [winfo width $p]
			}
		}
		BWidget::place $self(menu-fr) 0 0 center
	}
	
	if { [string equal [focus] $self(menu-fr)] || !$process } {
	focus -force $t_current
	}
	
	wm state $self(menu-fr) normal
	
	after idle "after 500 [list [namespace current]::show_menu 1]"
}

proc hidetabs::focus_ch {to} {
	variable self
	variable t_current
	variable p_fm
	
	if { ! [info exist t_current] } { return }
	
	set wk ""
	foreach key [array names p_fm] {
		if { [string equal $t_current $p_fm($key)] } {
			set wk $key
		}
	}
	
	if { [string length $wk] == 0 } {return}
	lassign $wk c r
	
	switch -- $to {
		up {
			set n [expr {$r - 1}]
			if { [info exist "p_fm($c $n)"] } {
				focus -force "$p_fm($c $n)"
				set t_current "$p_fm($c $n)"
			} else {
				set max 0
				foreach k [array names p_fm] {
					lassign $k nextc nextr
					if { $nextc == $c && $nextr > $max } {
						set max $nextr
					}
				}
				focus -force "$p_fm($c $max)"
				set t_current "$p_fm($c $max)"
			}		
		}
		down {
			set n [expr {$r + 1}]
			if { [info exist "p_fm($c $n)"] } {
				focus -force "$p_fm($c $n)"
				set t_current "$p_fm($c $n)"
			} else {
				focus -force "$p_fm($c 0)"
				set t_current "$p_fm($c 0)"
			}		
		}
		left {
			set n [expr {$c - 1}]
			if { [info exist "p_fm($n $r)"] } {
				focus -force "$p_fm($n $r)"
				set t_current "$p_fm($n $r)"
			} else {
				set max 0
				foreach k [array names p_fm] {
					lassign $k nextc nextr
					if { $nextr == $r && $nextc > $max } {
						set max $nextc
					}
				}
				focus -force "$p_fm($max $r)"
				set t_current "$p_fm($max $r)"
			}
		}
		right {
			set n [expr {$c + 1}]
			if { [info exist "p_fm($n $r)"] } {
				focus -force "$p_fm($n $r)"
				set t_current "$p_fm($n $r)"
			} else {
				focus -force "$p_fm(0 $r)"
				set t_current "$p_fm(0 $r)"
			}
		}
	}
}

proc hidetabs::get_tabs_data {} {
	variable self
	
	set result {}
	
	foreach tab [.nb pages] {
		set cw [pack slaves [.nb getframe $tab]]
		set chatid [::chat::winid_to_chatid $cw]
		set icon hidetabs/none-type
		set number_msg 0; set personal_msg 0
		
		if { [string length $chatid] > 0 } {
			set number_msg $::ifacetk::number_msg($chatid)
			set personal_msg $::ifacetk::personal_msg($chatid)
			set tabtitle $chat::chats(tabtitlename,$chatid)
			if { [chat::is_groupchat $chatid] ||  [chat::is_chat $chatid] } {
				set icon [::ifacetk::roster::get_jid_icon \
					[::chat::get_xlib $chatid] [::chat::get_jid $chatid]]
			}
		} else {
			set tabtitle [lindex [ButtonBar::itemcget $self(fr) $tab -text] 0]
		}
		
		lappend result [list $tab [ButtonBar::itemcget $self(fr) $tab -foreground] \
				$tabtitle $number_msg $personal_msg \
				"[ButtonBar::itemcget $self(fr) $tab -command];[namespace current]::restore" \
				$icon [$self(fr) raise]]
	}
	
	return $result
}

proc hidetabs::restore {} {
	catch {ifacetk::systray::restore}
}

proc hidetabs::ncmd_btn {code} {
	uplevel #0 $code
	menu_key 0 toggle
}

proc hidetabs::invoke_tab {w} {
	$w invoke
}
		
proc hidetabs::close_menu {} {
	variable self 
	variable config
	variable t_current
	
	if { [winfo exist $self(menu-fr)] } {
	destroy $self(menu-fr)
	}
	
	set self(in_r) 0  ;# reload window
	if { !$config(save_last_focus) } {
	catch {unset t_current}
	}
	
	focus -force [focus -lastfor .]
}

proc hidetabs::close { tb } {
	variable self
	if {[.nb raise] != ""} {
		eval destroy [pack slaves [$self(fr) getframe $tb]]
		$self(fr) delete $tb 1
		ifacetk::tab_move $self(fr) 0
	}
	close_menu
	show_menu
}
