# floatinglog.tcl --
#
#       This file implements Floating Log plugin for the Tkabber XMPP client.

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"]]
    variable 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) [winfo pixels . 18c] \
        [::msgcat::mc "Maximum window height."] \
        -command [list [namespace current]::set_window] \
        -type integer -group {Floating Log Window}

    custom::defvar options(width) [winfo pixels . 8c] \
        [::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 0.25em -padx 0.75m -pady 0.75m
        set first_message [lindex [pack slaves $winname] 0]
        if {$first_message == ""}  {
            pack $message_name -side bottom -fill x -padx 0.5m -pady 0.5m
        } else {
            pack $message_name -before $first_message \
                               -side bottom -fill x -padx 0.5m -pady 0.5m
        }

        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 0.25em -padx 1m -pady 1m
    }
    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 0.25em -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 0.5m -pady 0.5m
        }
        if {$from != ""} {
            pack $message_name_head -side bottom -fill x -padx 0.5m -pady 0
        }
    } else {
        if {$text != ""} {
            pack $message_name -before $first_message \
                               -side bottom -fill x -padx 0.5m -pady 0.5m
        }
        if {$from != ""} {
            pack $message_name_head -before $first_message \
                                    -side bottom -fill x -padx 0.5m -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::removeResource $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:et
