# $Id: floatinglog.tcl 2100 2014-01-26 09:49:59Z sergei $

package require msgcat

namespace eval floatinglog {
    ::msgcat::mcload [file join [file dirname [info script]] msgs]

    if {![::plugins::is_registered floatinglog]} {
	::plugins::register floatinglog \
			    -namespace [namespace current] \
			    -source [info script] \
			    -description [::msgcat::mc "Whether the Floating Log plugin is loaded."] \
			    -loadcommand [namespace code load] \
			    -unloadcommand [namespace code unload]
	return
    }

    variable id 0
    variable winname .floatinglog
    variable ignore_message_list {}
    lappend ignore_message_list [string map {%s *} [::msgcat::mc "Idle for %s"]]
    set request_from [string map {%s *} [::msgcat::mc "%s request from %s"]]
    lappend ignore_message_list [string replace $request_from \
			[string first * $request_from] \
			[string first * $request_from] iqibb]
    lappend ignore_message_list [string replace $request_from \
			[string first * $request_from] \
			[string first * $request_from] ibb]
    lappend ignore_message_list [string map {%s *} \
			[::msgcat::mc "Login retry for %s in %s"]]
    unset request_from

    #
    # Options section
    #
    custom::defgroup Plugins [::msgcat::mc "Plugins options."] \
	-group Tkabber
    custom::defgroup {Floating Log} \
	[::msgcat::mc "Floating Log plugin options."] \
	-group Plugins
    custom::defgroup {Floating Log Window} \
	[::msgcat::mc "Floating Log window properties."] \
	-group {Floating Log}
    custom::defgroup {Floating Log Control} \
	[::msgcat::mc "Message types to show."] \
	-group {Floating Log}

    custom::defvar options(show_log) 0 \
	[::msgcat::mc "Show floating log."] \
	-type boolean -group {Floating Log}

    custom::defvar options(show_chat) 1 \
	[::msgcat::mc "Show chat messages."] \
	-type boolean -group {Floating Log Control}

    custom::defvar options(show_groupchat) 1 \
	[::msgcat::mc "Show groupchat messages."] \
	-type boolean -group {Floating Log Control}

    custom::defvar options(show_other_messages) 1 \
	[::msgcat::mc "Show other messages."] \
	-type boolean -group {Floating Log Control}

    custom::defvar options(show_only_personal) 0 \
	[::msgcat::mc "Show only personal messages and MUC highlights."] \
	-type boolean -group {Floating Log Control}

    custom::defvar options(show_notifications) 1 \
	[::msgcat::mc "Show notifications (presence state changes,\
	    chat state events, IQ queries etc)."] \
	-type boolean -group {Floating Log Control}

    custom::defvar options(show_when_tkabber_is_active) 0 \
	[::msgcat::mc "Show when Tkabber is active."] \
	-type boolean -group {Floating Log Control}

    custom::defvar options(show_on_away_status) 1 \
	[::msgcat::mc "Show in \"Away\" status."] \
	-type boolean -group {Floating Log Control}

    custom::defvar options(show_on_xa_status) 1 \
	[::msgcat::mc "Show in \"Extended away\" status."] \
	-type boolean -group {Floating Log Control}

    custom::defvar options(show_on_dnd_status) 0 \
	[::msgcat::mc "Show in \"Do not disturb\" status."] \
	-type boolean -group {Floating Log Control}

    custom::defvar options(show_message_body) 1 \
	[::msgcat::mc "Show incoming message body."] \
	-type boolean -group {Floating Log Control}

    
    custom::defvar options(simple_show_scheme) 0 \
	[::msgcat::mc "Use simple view scheme."] \
	-type boolean -group {Floating Log Window}

    custom::defvar options(alpha) 80 \
	[::msgcat::mc "Window opacity (in percent)."] \
	-command [list [namespace current]::set_window] \
	-type integer -group {Floating Log Window}

    custom::defvar options(position) "-0-60" \
	[::msgcat::mc "Window position."] \
	-command [list [namespace current]::set_window] \
	-type string -group {Floating Log Window}

    custom::defvar options(max_height) 700 \
	[::msgcat::mc "Maximum window height."] \
	-command [list [namespace current]::set_window] \
	-type integer -group {Floating Log Window}

    custom::defvar options(width) 300 \
	[::msgcat::mc "Window width."] \
	-command [list [namespace current]::set_window] \
	-type integer -group {Floating Log Window}

    custom::defvar options(livetime) 5 \
	[::msgcat::mc "Delay before message hide (in seconds)."] \
	-type integer -group {Floating Log}
}

proc floatinglog::load {} {

    hook::add set_status_hook [namespace current]::process_status
    hook::add process_message_hook [namespace current]::process_message
}

proc floatinglog::unload {} {
    variable id
    variable winname
    variable ignore_message_list

    hook::remove set_status_hook [namespace current]::process_status
    hook::remove process_message_hook [namespace current]::process_message

    catch {destroy $winname}
    catch {unset id}
    catch {unset winname}
    catch {unset ignore_message_list}
}

#
# Floating window initialization
#
proc floatinglog::window_initialization {} {
    variable options
    variable winname

    if {[winfo exists $winname]} {
	destroy $winname
    }

    toplevel $winname -bd 0 -class Balloon
    if {$::tcl_platform(platform) == "macintosh"} {
	catch {unsupported1 style $winname floating sideTitlebar}
    } elseif {$::aquaP} {
	::tk::unsupported::MacWindowStyle style $winname help none    
    } else {
	wm transient $winname .
	wm overrideredirect $winname 1
    }

    catch {
	if {[lsearch -exact [wm attributes $winname] -topmost] >= 0} {
	    wm attributes $winname -topmost 1
	}
    }

    set_window
    bind $winname <<ContextMenu>> [namespace current]::right_click
    bind $winname <Double-ButtonPress-1> [namespace current]::left_double_click
}

proc floatinglog::right_click {} {
    variable winname
    foreach id [after info] {
	if {[string first [namespace current]::del_text [after info $id]] >= 0} {
	    after cancel $id
	}
    }
    if {[winfo exists $winname]} {
	destroy $winname
    }
}
 
proc floatinglog::left_double_click {} {
    ::ifacetk::systray::restore
    if {[focus] == ""} {
	focus -force .
    }
}

proc floatinglog::left_double_click_message {xlib jid type} {
    switch -- $type {
	groupchat {
	}
	chat {
	    chat::open_to_user $xlib $jid
	}
	message {
	    #message::send_dialog -to $jid
	}
    }
}

proc floatinglog::add_text {text {from ""} {xlib ""} {type ""} \
				 {is_subject ""} {subject ""}} {
    variable options
    variable id
    variable winname
    variable logfileId

    if {!($options(show_log))} {return}

    set active_window ""
    set active_window [focus]
    if {$active_window != "" && !$options(show_when_tkabber_is_active)} return

    if {!$options(show_on_away_status) && $::curuserstatus == "away"} return

    if {!$options(show_on_xa_status) && $::curuserstatus == "xa"} return

    if {!$options(show_on_dnd_status) && $::curuserstatus == "dnd"} return

    if {![winfo exists $winname]} {
	window_initialization
    }
    incr id
    if {$options(simple_show_scheme)} {
	add_text_scheme_simple $text $from $xlib $type $is_subject $subject
    } else {
	add_text_scheme_01 $text $from $xlib $type $is_subject $subject
    }
}

proc floatinglog::add_text_scheme_simple {body from xlib type is_subject subject} {
    variable options
    variable id
    variable winname
    variable logfileId

    if { $from != "" } {
	if {[catch {::chat::get_nick $xlib $from $type} nick]} {
	    set nick [chat::get_nick $from $type]
	}
	if {!$options(show_message_body)} {
	    set body [::msgcat::mc "Incoming message"]
	}
	set text [::msgcat::mc "From: "]
	set formattext [append text  $nick " (" $from ")\n" "\n" $body]
    } else {
	set text $body
    }

    if {$text != ""} {
	set message_name $winname.msg$id
	message $message_name \
		-text $text \
		-width [expr $options(width) - 10] \
		-justify left \
		-relief solid -bd 1 -padx 3 -pady 3
	set first_message [lindex [pack slaves $winname] 0]
	if {$first_message == ""}  {
	    pack $message_name -side bottom -fill x -padx 2 -pady 2
	} else {
	    pack $message_name -before $first_message \
			       -side bottom -fill x -padx 2 -pady 2
	}

	if {($xlib != "") && ($from != "") && ($type != "")} {
	    bind $message_name <Double-ButtonPress-1> \
		 +[list [namespace current]::left_double_click_message \
			$xlib [double% $from] [double% $type]]
	}
	after [expr {$options(livetime) * 1000}] \
	      [namespace current]::del_text "$message_name"
    }
} 

proc floatinglog::add_text_scheme_01 {text from xlib type is_subject subject} {
    variable options
    variable id
    variable winname
    variable logfileId


    if {$text != ""} {
	if { ! $options(show_message_body) && $from != "" } {
	    set text [::msgcat::mc "Incoming message"]
	}
	set message_name $winname.msg$id
	message $message_name \
		-text $text \
		-width [expr $options(width) - 10] \
		-justify left \
		-relief solid -bd 1 -padx 3 -pady 3
    }
    if {$from != ""} {
	if {[catch {::chat::get_nick $xlib $from $type} nick]} {
	    set nick [chat::get_nick $from $type]
	}
	if {$nick != "" } {
	    append nick " (" $from ")"
	} else {
	    set nick $from
	}
	set message_name_head $winname.msg${id}head
	message $message_name_head \
		-text $nick \
		-width [expr $options(width) - 10] \
		-justify left \
		-relief flat -bd 1 -padx 0 -pady 0
    }
    set first_message [lindex [pack slaves $winname] 0]
    if {$first_message == ""}  {
	if {$text != ""} {
	    pack $message_name -side bottom -fill x -padx 2 -pady 2
	}
	if {$from != ""} {
	    pack $message_name_head -side bottom -fill x -padx 2 -pady 0
	}
    } else {
	if {$text != ""} {
	    pack $message_name -before $first_message \
			       -side bottom -fill x -padx 2 -pady 2
	}
	if {$from != ""} {
	    pack $message_name_head -before $first_message \
				    -side bottom -fill x -padx 2 -pady 0
	}
    }

    if {($xlib != "") && ($from != "") && ($type != "")} {
	if {$from != ""} { 
	    bind $message_name_head <Double-ButtonPress-1> \
		 +[list [namespace current]::left_double_click_message \
			$xlib [double% $from] [double% $type]]
	}
	if {$text != "" } { 
	    bind $message_name <Double-ButtonPress-1> \
		 +[list [namespace current]::left_double_click_message \
			$xlib [double% $from] [double% $type]]
	}
    }
    if {$text != ""} {
	after [expr {$options(livetime) * 1000}] \
	      [namespace current]::del_text "$message_name"
    }
}

proc floatinglog::process_status {text} {
    variable ignore_message_list
    variable options

    if {!$options(show_notifications)} return
    foreach ignore_message $ignore_message_list {
	if {[string match $ignore_message $text]} return
    }

    add_text $text
}

proc floatinglog::del_text {{msg ""}} {
    variable winname

    if {[winfo exists $msg]} {
	pack forget $msg
	destroy $msg
    }
    append msg head
    if {[winfo exists $msg]} {
	pack forget $msg
	destroy $msg
    }
    if {[winfo children $winname] == ""} {
	if {[winfo exists $winname]} {
	    destroy $winname
	}
    }
}

proc floatinglog::process_message {xlib from id type is_subject \
				   subject body err thread priority x} {
    variable options

    if {$body != ""} {
	switch -exact -- $type {
	    chat {
		if {!$options(show_chat)} return
	    }
	    groupchat { 
		if {!$options(show_groupchat)} return
		if {[::xmpp::delay::exists $x]} return
		if {![catch {
		    ::plugins::mucignore::is_ignored $xlib $from $type
		} ignore] && $ignore != ""} return
		if {$options(show_only_personal)} {
		    set chatid [chat::chatid $xlib \
			[::xmpp::jid::stripResource $from]]
		    set myjid [chat::our_jid $chatid]
		    set mynick [chat::get_nick $xlib $myjid $type]
		    if {![check_message $mynick $body]} return
		}
	    }
	    default {
		if {!$options(show_other_messages)} return
	    }
	}

	catch {
	    add_text $body $from $xlib $type $is_subject $subject
	}
    }
}


proc floatinglog::set_window {args} {
    variable options
    variable winname

    if {![winfo exists $winname]} {
	return
    }
    catch {
	if {[lsearch -exact [wm attributes $winname] -alpha] >= 0} {
	    wm attributes $winname -alpha [expr $options(alpha) / 100.0]
	}
    }
    wm minsize $winname $options(width) 10
    wm maxsize $winname $options(width) $options(max_height)
    wm geometry $winname $options(position)
    raise $winname
} 

# vim:ts=8:sw=4:sts=4:noet
