Dialog class

jbelljbell
Added on 2009-02-04 19:15:46
Dialog class - Show - Edit - Download
##########################################################################
#  This file is part of AmarokNowPlaying pour Facebook - Show what you   #
#  are listening to on your Facebook profile.                            #
#  Copyright (C) 2008 Jany Belluz <jany.belluz@hotmail.fr>               #
#                                                                        #
#  This program is free software: you can redistribute it and/or modify  #
#  it under the terms of the GNU General Public License as published by  #
#  the Free Software Foundation, either version 3 of the License, or     #
#  (at your option) any later version.                                   #
#                                                                        #
#  This program is distributed in the hope that it will be useful,       #
#  but WITHOUT ANY WARRANTY; without even the implied warranty of        #
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
#  GNU General Public License for more details.                          #
#                                                                        #
#  You should have received a copy of the GNU General Public License     #
#  along with this program.  If not, see <http://www.gnu.org/licenses/>. #
##########################################################################
#  Dialog library (uses `kdialog` and/or `zenity`)                       #
##########################################################################
 
# Examples:
# To show a text information dialog, use:
# Dialog.message("My title", "My text")
 
class Dialog
        
        # Was this class initialized?
        # La classe est-elle prête ?
        @@ready = false
        
        # Which programs are available?
        # Quels programmes sont disponibles ?
        @@available = []
        
        # Which program is used? (determined after automated initialization)
        # Quel programme est utilisé ? (choisi après l'initialisation de la classe)
        @@program = nil
        
        # What to put before titles
        # Préfixe des titres
        @@pre_title = 'Dialog -'
        # use Dialog.pre_title = 'pouet'
        
        # Debug mode
        @@debug = false
        
        # Constants for return codes
        # Constantes de codes de retour
        OK_YES_CONTINUE = 0
        NO_CANCEL_CLOSE = 1
        ERROR = 2
        ZENITY_TIMED_OUT = 3
        
        # Last return code, filled on each call to a function
        # Dernier code de retour, déterminé à chaque appel de fonction
        @@last_return_code = ERROR
        # use Dialog.last_return_code
        
        # Last output, filled on each call to a function
        # Dernière information renvoyée, déterminée à chaque appel de fonction
        @@last_out = ''
        attr_reader :last_out
        
        def Dialog.init
                # External Dependencies
                # TODO Redirect output
                if system("kdialog", "--version") 
                @@program = 'kdialog'
                @@available.to_a.push 'kdialog'
                puts '"kdialog" found' if @@debug
                end
                
                if system("zenity", "--version")
                @@program = 'zenity' if @@program == nil
                @@available.to_a.push 'zenity'
                puts '"zenity" found' if @@debug
                end
                
                puts @@program.inspect if @@debug
                puts @@available.inspect if @@debug
                
                @@ready = true
        end
        
        def Dialog.set_prefered( prefered_prog = 'kdialog' )
                Dialog.init unless @@ready
                if @@available.to_a.include? prefered_prog.to_s
                        @@program = prefered_prog.to_s
                end
        end
        
        def Dialog.pre_title= (pre_title)
                @@pre_title = pre_title.to_s.chomp
                @@pre_title['"'] = '\"' if @@pre_title.include? '"'
        end
        
        def Dialog.pre_title
                @@pre_title
        end
        
        def Dialog.last_return_code
                @@last_return_code
        end
        
        # Type must must be one of 'info', 'warning', 'error', 'question'
        # Returns nothing
        def Dialog.message( message, title = '', type = 'info' )
                Dialog.init unless @@ready
                
                title = Dialog.title_ize(title)
                message = Dialog.sanitize(message)
                type = type.to_s
                type = 'info' unless [ 'info', 'warning', 'error', 'question' ].include?(type)
                
                if @@program == 'kdialog'
                        type = 'msgbox' if type == 'info'
                        type = 'sorry' if type == 'warning'
                        type = 'yesno' if type == 'question'
                        
                        @@last_out = `kdialog --title "#{ title }" --#{ type } "#{ message }"`.to_s.chomp
                        @@last_return_code = Dialog.from_kdialog_return_code($?.exitstatus)
                elsif @@program == 'zenity'
                        @@last_out = `zenity --title="#{ title }" --#{ type } --text="#{ message }"`.to_s.chomp
                        @@last_return_code = Dialog.from_zenity_return_code($?.exitstatus)
                end
        end
        
        # 'hide_input' must be true or false
        # Returns the entered string
        def Dialog.input( message, title = '', default = '', hide_input = false )
                Dialog.init unless @@ready
                
                title = Dialog.title_ize(title)
                message = Dialog.sanitize(message)
                default = Dialog.sanitize(default)
                
                if @@program == 'kdialog'
                        type = (hide_input ? 'password' : 'inputbox')
                        @@last_out = `kdialog --title "#{ title }" --#{ type } "#{ message }" "#{ default }"`.to_s.chomp
                        @@last_return_code = Dialog.from_kdialog_return_code($?.exitstatus)
                elsif @@program == 'zenity'
                        @@last_out = `zenity --entry --title="#{ title }" --text="#{ message }" --entry-text="#{ default }" #{ hide_input ? '--hide-text' : '' }`.to_s.chomp
                        @@last_return_code = Dialog.from_zenity_return_code($?.exitstatus)
                end
                
                return @@last_out
        end
        
        # Entries must be an array, it returns the selected element(s)
        # Type must be one of 'radio' (= one selection), 'check' (= many)
        # Selected must be one element or an array of elements from entries, wich
        # will be pre-selected.
        # Returns an array of chosen elements, even if only one was chosen.
        def Dialog.chooser( message, entries, title = '', type = 'radio', selected = [] )
                Dialog.init unless @@ready
                
                @@last_out = []
                
                # Only one entry, why to ask the user for his choice ?
                if ! entries.respond_to? 'each_index'
                        @@last_out = [ entries.to_s ]
                        return @@last_out
                end
                
                title = Dialog.title_ize(title)
                message = Dialog.sanitize(message)
                type = type.to_s
                type = 'radio' unless [ 'radio', 'check' ].include?(type)
                
                entries_list = ''
                
                if @@program == 'kdialog'
                        if selected.respond_to? 'each_index' # 'selected' is supposed to be an array of strings
                                entries.each_index do |i|
                                        entries_list += "#{ i } \"#{ Dialog.sanitize(entries[i]) }\" " +
                                                ( selected.include?(entries[i].to_s) ? 'on ' : 'off ' ).to_s
                                end
                        else
                                entries.each_index do |i|
                                        entries_list += "#{ i } \"#{ Dialog.sanitize(entries[i]) }\" " +
                                                ( entries[i].to_s == selected.to_s ? 'on ' : 'off ' ).to_s
                                end
                        end
                        
                        puts indexes = `kdialog --title "#{ title }" --#{ type }list "#{ message }" #{ entries_list }`.chomp.to_s.split('" "')
                        indexes.each { |i| @@last_out.push entries[i.to_i].to_s }
                        @@last_return_code = Dialog.from_kdialog_return_code($?.exitstatus)
                        # `exit #{ @@last_return_code }` # Sets $?.exitstatus to this class standard ones FIXME
                        
                elsif @@program == 'zenity'
                        if selected.respond_to? 'each_index' # 'selected' is supposed to be an array of strings
                                entries.each_index do |i|
                                        entries_list += ( selected.include?(entries[i].to_s) ? 'TRUE ' : 'FALSE ' ).to_s +
                                                "\"#{ Dialog.sanitize(entries[i]) }\" "
                                end
                        else
                                entries.each_index do |i|
                                        entries_list +=  ( entries[i].to_s == selected.to_s ? 'TRUE ' : 'FALSE ' ).to_s +
                                                "\"#{ Dialog.sanitize(entries[i]) }\" "
                                end
                        end
                
                        @@last_out = `zenity --list --#{ type }list --title="#{ title }" --text="#{ message }" --column="" --column="" #{ entries_list }`.chomp.split('|')
                        @@last_return_code = Dialog.from_zenity_return_code($?.exitstatus)
                end
                
                return @@last_out
        end
        
        
        private # Utilities
        
        def Dialog.from_kdialog_return_code( code )
                code = code.to_i
                if code == 0
                        return OK_YES_CONTINUE
                elsif code == 1 || code == 2
                        return NO_CANCEL_CLOSE
                else
                        return ERROR
                end
        end
        
        def Dialog.from_zenity_return_code( code )
                code = code.to_i
                if code == 0
                        return OK_YES_CONTINUE
                elsif code == 1
                        return NO_CANCEL_CLOSE
                elsif code == 5
                        return ZENITY_TIMED_OUT
                else
                        return ERROR
                end
        end
        
        def Dialog.title_ize( title )
                return  Dialog.sanitize( @@pre_title.to_s + ((' ' + title.to_s + '.') unless title.to_s.empty?).to_s )
        end
        
        def Dialog.sanitize( text )
                text = text.to_s
                text['"'] = '\"' if text.include? '"'
                return text
        end
end
 
# Tests
# Dialog.set_prefered( 'kdialog' )
# 
# puts Dialog.chooser("Pouet", [ 'banane', 'pomme', 'poire' ], 'Test', 'check', [ 'banane', 'pomme' ]).inspect
# puts Dialog.chooser("Pouet", [ 'banane', 'pomme', 'poire' ], 'Test', 'radio', [ 'banane', 'pomme' ]).inspect
# puts Dialog.chooser("Pouet", [ 'banane', 'pomme', 'poire' ], 'Test', 'radio').inspect
# puts Dialog.chooser("Pouet", [ 'banane', 'pomme', 'poire' ], 'Test').inspect
# puts Dialog.chooser("Pouet", [ 1, 'pomme', :Figue ], 'Test').inspect
# puts Dialog.message("Pouet", "Test").inspect
# puts Dialog.message("Pouet", "Test", "warning").inspect
# puts Dialog.message("Pouet ?", "Test", "question").inspect
# puts Dialog.message("Pouet !!", "Test", "error").inspect
# puts Dialog.input("Pouet !!", "Test II", true).inspect
# 
# Dialog.set_prefered( 'zenity' )
# 
# puts Dialog.chooser("Pouet", [ 'banane', 'pomme', 'poire' ], 'Test', 'check', [ 'banane', 'pomme' ]).inspect
# puts Dialog.chooser("Pouet", [ 'banane', 'pomme', 'poire' ], 'Test', 'radio', [ 'banane', 'pomme' ]).inspect
# puts Dialog.chooser("Pouet", [ 'banane', 'pomme', 'poire' ], 'Test', 'radio').inspect
# puts Dialog.chooser("Pouet", [ 'banane', 'pomme', 'poire' ], 'Test').inspect
# puts Dialog.chooser("Pouet", [ 1, 'pomme', :Figue ], 'Test').inspect
# puts Dialog.message("Pouet", "Test").inspect
# puts Dialog.message("Pouet", "Test", "warning").inspect
# puts Dialog.message("Pouet ?", "Test", "question").inspect
# puts Dialog.message("Pouet !!", "Test", "error").inspect
# puts Dialog.input("Pouet !!", "Test II", true).inspect
# 
# puts 'Done.'