#!/usr/bin/env ruby

# rbqtsh : a graphical shell for qtruby.
#
# author: Germain Garand <germain@ebooksfrance.org>
# ruby port: Alexander Kellett <lypanov@kde.org>
# license: GNU Public License v2
#

require 'stringio'

case File.basename $0
when "rbqtapi"
   require 'Qt'
when "rbkdeapi"
   require 'Korundum'
end

include Qt

$image0_data =
["22 22 7 1",
". c None",
"# c #000000",
"b c #292c29",
"c c #5a5d5a",
"d c #838583",
"e c #c5c2c5",
"a c #ffffff",
"......................",
"....##########........",
"....#aaaaaaa#b#.......",
"....#aaaaaaa#cb#......",
"....#aaaaaaa#dcb#.....",
"....#aaaaaaa#edcb#....",
"....#aaaaaaa#aedcb#...",
"....#aaaaaaa#######...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....###############...",
"......................",
"......................"]

$image1_data =
["22 22 5 1",
". c None",
"# c #000000",
"c c #838100",
"a c #ffff00",
"b c #ffffff",
"......................",
"......................",
"......................",
"............####....#.",
"...........#....##.##.",
"..................###.",
".................####.",
".####...........#####.",
"#abab##########.......",
"#babababababab#.......",
"#ababababababa#.......",
"#babababababab#.......",
"#ababab###############",
"#babab##cccccccccccc##",
"#abab##cccccccccccc##.",
"#bab##cccccccccccc##..",
"#ab##cccccccccccc##...",
"#b##cccccccccccc##....",
"###cccccccccccc##.....",
"##cccccccccccc##......",
"###############.......",
"......................"]

$image2_data =
["22 22 5 1",
". c None",
"# c #000000",
"a c #838100",
"b c #c5c2c5",
"c c #cdb6d5",
"......................",
".####################.",
".#aa#bbbbbbbbbbbb#bb#.",
".#aa#bbbbbbbbbbbb#bb#.",
".#aa#bbbbbbbbbcbb####.",
".#aa#bbbccbbbbbbb#aa#.",
".#aa#bbbccbbbbbbb#aa#.",
".#aa#bbbbbbbbbbbb#aa#.",
".#aa#bbbbbbbbbbbb#aa#.",
".#aa#bbbbbbbbbbbb#aa#.",
".#aa#bbbbbbbbbbbb#aa#.",
".#aaa############aaa#.",
".#aaaaaaaaaaaaaaaaaa#.",
".#aaaaaaaaaaaaaaaaaa#.",
".#aaa#############aa#.",
".#aaa#########bbb#aa#.",
".#aaa#########bbb#aa#.",
".#aaa#########bbb#aa#.",
".#aaa#########bbb#aa#.",
".#aaa#########bbb#aa#.",
"..##################..",
"......................"]

$image3_data =
["22 22 88 2",
"Qt c None",
".2 c #000000",
".S c #08ff08",
"#v c #100810",
".U c #101010",
"#c c #101018",
".M c #181018",
"#e c #181818",
".A c #181820",
".L c #201820",
"#l c #202020",
".z c #202029",
"#m c #292029",
"#u c #292829",
"#n c #292831",
".R c #29ff29",
"#o c #312831",
".T c #313031",
"#p c #313039",
".Z c #31ff31",
"#q c #393039",
"#t c #393839",
".y c #393841",
"#s c #413841",
".o c #414041",
"#h c #4a4852",
".n c #5a505a",
"#r c #5a5962",
".I c #5ace5a",
"#b c #6a616a",
".p c #6a696a",
".x c #6a6973",
".Y c #6aff62",
".l c #736973",
".t c #7b717b",
".s c #7b7183",
".0 c #7bff7b",
".r c #837983",
".u c #83798b",
"#g c #83858b",
".v c #8b7994",
"#i c #8b858b",
".w c #8b8594",
"#j c #8b8d8b",
".8 c #8b8d94",
".m c #948d94",
"#k c #948d9c",
"#f c #949594",
".q c #94959c",
".J c #94c694",
"#d c #9c959c",
"#a c #9c95a4",
".k c #9c9d9c",
".N c #9c9da4",
".H c #9ccea4",
".K c #a49da4",
"#. c #a49dac",
".i c #a4a5a4",
".3 c #a4a5ac",
"## c #ac9dac",
".V c #aca5ac",
".d c #acaeac",
".j c #acaeb4",
".9 c #b4aeb4",
".# c #b4b6b4",
".a c #bdbebd",
".7 c #bdd6bd",
".c c #c5c6c5",
".5 c #cdc6cd",
".b c #cdcecd",
".4 c #cdced5",
".F c #d5ced5",
".G c #d5cede",
".h c #d5d6d5",
".E c #d5d6de",
".Q c #d5ffd5",
".B c #ded6de",
".1 c #ded6e6",
".g c #dedede",
".D c #dedee6",
".6 c #e6dee6",
".f c #e6e6e6",
".C c #e6e6ee",
".X c #e6ffe6",
".O c #eee6ee",
".e c #eeeeee",
".W c #f6f6f6",
".P c #ffffff",
"QtQtQtQtQtQt.#.a.b.b.b.b.c.c.a.a.d.aQtQtQtQt",
"QtQtQtQtQtQt.a.e.f.f.f.f.f.e.e.e.g.aQtQtQtQt",
"QtQtQtQtQtQt.a.c.c.c.b.b.c.c.c.c.a.cQtQtQtQt",
"QtQtQtQtQtQt.#.a.a.a.a.#.a.a.#.#.d.aQtQtQtQt",
"QtQtQtQtQt.c.d.c.a.c.c.c.a.a.a.c.#QtQtQtQtQt",
"QtQtQtQtQt.a.a.#.a.a.a.a.a.a.c.c.#QtQtQtQtQt",
"QtQtQtQtQt.a.#.c.a.a.a.a.a.c.a.c.dQtQtQtQtQt",
"QtQtQtQtQt.c.a.a.a.a.a.a.a.a.a.a.#QtQtQtQtQt",
"QtQtQtQtQt.d.b.f.g.g.g.g.g.g.h.g.i.i.jQtQtQt",
"QtQtQt.a.k.l.#.h.b.h.b.h.b.h.g.g.m.n.o.p.#Qt",
"QtQt.a.q.r.s.t.t.t.t.t.t.t.u.v.w.x.y.z.A.o.i",
"Qt.a.k.B.C.D.B.E.E.E.E.F.G.H.I.J.K.o.L.L.M.y",
".a.N.O.P.P.P.P.P.P.P.P.P.Q.R.S.R.b.v.T.A.U.L",
".V.W.P.P.P.P.P.P.P.P.P.P.X.Y.Z.0.P.1.t.A.2.L",
".3.E.4.5.4.h.E.E.g.6.D.B.D.E.7.F.4.5.8.M.2.A",
".m.9.j.V.3#..3.K#.#..i#..K#.###a.q.8#b#c.2.L",
".m.j.j#..3.K.K.K.N.K.N.N.N.N#a#d#d.w#b#c.2#e",
"#f#.#..K.N.K.N.N.N#a.k#a#d#d#d#a.m#g#b.M.2#h",
".m.3.K.K#a.k#a#d#a.k#a#d#a#d.q.m.8#i.x#c#e.d",
"#f#g#i.w#j.w#i.8.w#i.8.8.m.8.m#k.8.w#b#e#fQt",
".#.l.z.A#l.z#m#m#m#n#o#o#p#p#q#q#p#o#p#fQtQt",
"QtQt.d#r#s#s#t#p.T.T.T#u#u.z#e#e#v.o.kQtQtQt"]


class QtShellControl < Qt::MainWindow
   attr_accessor :menubar, :fileMenu, :helpMenu, :toolBar, :fileName, :fileOpenAction
   attr_accessor :fileSaveAction, :fileSaveAsAction, :filePrintAction, :fileExitAction
   attr_accessor :helpExampleAction, :comboBox, :sessionLog, :executedLines, :printer
   slots 'fileOpen()', 'fileOpen()', 'fileSave()', 'fileSaveAs()'
   slots 'filePrint()', 'fileExit()', 'runSelection()', 'helpExample()'
   signals 'fileNeedsEval(QString)', 'selection(QString)'

    def initialize(*k)
        super(*k)
        image0 = Qt::Pixmap.new($image0_data)
        image1 = Qt::Pixmap.new($image1_data)
        image2 = Qt::Pixmap.new($image2_data)
        image3 = Qt::Pixmap.new($image3_data)
        box = VBox.new(self)
        @sessionLog = TextEdit.new(box, "sessionLog")
        @sessionLog.setTextFormat(Qt::RichText)
        @sessionLog.setReadOnly(true)
        @comboBox = ComboBox.new(box, "comboBox")
        @comboBox.setEditable(true)
        @comboBox.setAutoCompletion(true)
        self.setCentralWidget(box)
        @comboBox.setFocus
        self.resize(500,300)
        self.setCaption("QtRuby Shell")
        def trUtf8(k)
            return k
        end
        @fileOpenAction = Qt::Action.new(self, "fileOpenAction")
        @fileOpenAction.setIconSet(Qt::IconSet.new(image1))
        @fileOpenAction.setText(trUtf8("Open"))
        @fileOpenAction.setMenuText(trUtf8("&Open..."))
        @fileOpenAction.setAccel(KeySequence.new(trUtf8("Ctrl+O")))
        @fileSaveAction = Qt::Action.new(self, "fileSaveAction")
        @fileSaveAction.setIconSet(Qt::IconSet.new(image2))
        @fileSaveAction.setText(trUtf8("Save"))
        @fileSaveAction.setMenuText(trUtf8("&Save"))
        @fileSaveAction.setAccel(KeySequence.new(trUtf8("Ctrl+S")))
        @fileSaveAsAction = Qt::Action.new(self, "fileSaveAsAction")
        @fileSaveAsAction.setText(trUtf8("Save As"))
        @fileSaveAsAction.setMenuText(trUtf8("Save &As..."))
        @fileSaveAsAction.setAccel(KeySequence.new(trUtf8("Ctrl+A")))
        @filePrintAction = Qt::Action.new(self, "filePrintAction")
        @filePrintAction.setIconSet(Qt::IconSet.new(image3))
        @filePrintAction.setText(trUtf8("Print"))
        @filePrintAction.setMenuText(trUtf8("&Print..."))
        @filePrintAction.setAccel(KeySequence.new(trUtf8("Ctrl+P")))
        @fileExitAction = Qt::Action.new(self, "fileExitAction")
        @fileExitAction.setText(trUtf8("Exit"))
        @fileExitAction.setMenuText(trUtf8("E&xit"))
        @fileExitAction.setAccel(KeySequence.new(trUtf8("Ctrl+E")))

        @runAction = Qt::Action.new(self, "runAction");
        @runAction.setText(trUtf8("Run Selection"));
        @runAction.setMenuText(trUtf8("&Run Selection"));
        @runAction.setAccel(KeySequence.new(trUtf8("Ctrl+R")));

        @helpExampleAction = Qt::Action.new(self, "helpExampleAction");
        @helpExampleAction.setText(trUtf8("Example"));
        @helpExampleAction.setMenuText(trUtf8("Examp&le"));
        @helpExampleAction.setAccel(KeySequence.new(trUtf8("Ctrl+L")));
       
        @toolBar = Qt::ToolBar.new("", self, DockTop)
        @toolBar.setLabel(trUtf8("Tools"))
       
        @fileOpenAction.addTo(toolBar)
        @fileSaveAction.addTo(toolBar)
        @filePrintAction.addTo(toolBar)
       
        @menubar= Qt::MenuBar.new(self, "menubar")
       
        @fileMenu= Qt::PopupMenu.new(self)
        @fileOpenAction.addTo(fileMenu)
        @fileSaveAction.addTo(fileMenu)
        @fileSaveAsAction.addTo(fileMenu)
        @fileMenu.insertSeparator
        @filePrintAction.addTo(fileMenu)
        @fileMenu.insertSeparator
        @fileExitAction.addTo(fileMenu)
        @menubar.insertItem(trUtf8("&File"), fileMenu)
       
        @menubar.insertSeparator

        @runMenu= Qt::PopupMenu.new(self)
        @runAction.addTo(@runMenu)
        @menubar.insertItem(trUtf8("Run"), @runMenu)
        
		@menubar.insertSeparator

        @helpMenu= Qt::PopupMenu.new(self)
        @helpExampleAction.addTo(helpMenu)
        @menubar.insertItem(trUtf8("&Help"), helpMenu)

        connect(@fileOpenAction, SIGNAL('activated()'), self, SLOT('fileOpen()'))
        connect(@fileSaveAction, SIGNAL('activated()'), self, SLOT('fileSave()'))
        connect(@fileSaveAsAction, SIGNAL('activated()'), self, SLOT('fileSaveAs()'))
        connect(@filePrintAction, SIGNAL('activated()'), self, SLOT('filePrint()'))
        connect(@fileExitAction, SIGNAL('activated()'), self, SLOT('fileExit()'))
        connect(@runAction, SIGNAL('activated()'), self, SLOT('runSelection()'))
        connect(@helpExampleAction, SIGNAL('activated()'), self, SLOT('helpExample()'))

        @executedLines = []
    end

    def fileOpen
        fname = Qt::FileDialog::getOpenFileName(
                                    ".",
                                    "Rbqtsh Session (*.rbqts)",
                                    self,
                                    "open session",
                                    "Choose a file to open")
        return if fname.nil?
        emit fileNeedsEval(fname)
    end

    def getFileName
        @fileName = Qt::FileDialog::getSaveFileName(
                                        ".",
                                        "Rbqtsh Session (*.rbqts)",
                                        self,
                                        "save session",
                                        "Choose a filename" )
        unless @fileName =~ /\.rbqts$/
            @fileName << ".rbqts"
        end
        @fileName
    end

    def save(fname)
        file = File.new(fname, "w")
        if file.nil
            # TODO fix ": unknown" to give a reason
            Qt::MessageBox::critical( 
                                 self, 
                                 "Error" , 
                                 "Couldn't open #{fname} for writing: unknown",  
                                 Qt::MessageBox::Ok, 
                                 Qt::MessageBox::NoButton )
            return
        end
        @executedLines.each {
            |line|
            next if line =~ /^\s*$/
            line.chomp!
            line << ";" unless line =~ /;\s*$/
            file.puts(line)
        }
        file.close
    end

    def fileSave
        return if emptySession
        fname = @fileName || getFileName
        return if fname.nil?
        save fname
    end

    def fileSaveAs(cond = 0)
        return if emptySession
        fname = getFileName
        return if fname.nil?
        ret = nil
        if File.exists(fname)
            cond += 1
            ret = Qt::MessageBox::warning(
                                     self,
                                     "Warning" ,
                                     "File exists, overwrite ?",
                                     Qt::MessageBox::Yes,
                                     Qt::MessageBox::No )
        else
            cond = 0
        end
        fileSaveAs(cond) if (cond == 0) and ret == Qt::MessageBox::No
        save(fname)
    end

	def filePrint()
		margin = 10
		pageNo = 1
		emptySession() and return
		printer = Qt::Printer.new()
		if printer.setup(self)
			statusBar().message( "Printing..." )
			p = Qt::Painter.new()
			if ! p.begin( printer )
				statusBar().message( "An error occured..." )
				return
			end
			
			p.setFont( sessionLog.font() )
			yPos = 0
			fm = p.fontMetrics
			metrics = Qt::PaintDeviceMetrics.new( printer )
			
			for i in 0..@executedLines.length-1
            	if  margin + yPos > metrics.height() - margin
                	msg ="Printing (page "+ ++pageNo + ")..."
                	statusBar().message( msg )
                	printer.newPage()             
                	yPos = 0                      
            	end
            	p.drawText( margin, margin + yPos,
                        	metrics.width(), fm.lineSpacing(),
                        	ExpandTabs | DontClip,
                        	"#{@executedLines[ i ]}" )
            	yPos = yPos + fm.lineSpacing()
        	end
        	p.end()       
        	statusBar().message( "Printing completed", 3000 )
    	else
        	statusBar().message( "Printing aborted", 3000 )
    	end
	end

    def fileExit
        emit $qApp.quit if confirmExit
    end

    def closeEvent(e)
       if confirmExit
           e.accept
       else
           e.ignore
        end
    end

    def confirmExit
        ret = 0
        return true if @executedLines.empty?
        ret = Qt::MessageBox::warning(
                                self,
                                "Warning" ,
                                "A session is opened, quit anyway ?",
                                 Qt::MessageBox::Yes,
                                 Qt::MessageBox::No)
        return ret != Qt::MessageBox::No
    end

    def emptySession
        empty = @executedLines.empty?
        statusBar.message("Session is empty...", 3000) if empty
        empty
    end

    def runSelection
		emit selection(@sessionLog.selectedText())
    end

    def helpExample
        emit fileNeedsEval("__DATA__")
    end

end

class QtShell < Qt::MainWindow
    slots 'evalInput()', 'evalFile(QString)', 'evalSelection(QString)'
    attr_accessor :shellWindow
    @@binding = binding
def bind
@@binding
end

def initialize(*k)
    super(*k)

    @shellWindow = QtShellControl.new(nil, "shellWindow")
    self.resize(350,350)
    self.move(Point.new(10,10))
    @shellWindow.move(Point.new(300,200))
    self.show
    @shellWindow.show

    connect(@shellWindow.comboBox.lineEdit, SIGNAL('returnPressed()'), self, SLOT('evalInput()'))

    @prompt = '<b><font color="blue">$&gt;</font></b>'
    self.setCaption("MainWindow - this")
    @shellWindow.sessionLog.setText("Ready.<br>")
    connect(@shellWindow, SIGNAL('fileNeedsEval(QString)'), self, SLOT('evalFile(QString)'))
    connect(@shellWindow, SIGNAL('selection(QString)'), self, SLOT('evalSelection(QString)'))
end

def logAppend(str)
    tmp = @shellWindow.sessionLog.text << str
    @shellWindow.sessionLog.setText tmp
end

def evalInput
    evalOneLine @shellWindow.comboBox.currentText
end

=begin

sub TIEHANDLE { my ( $classnm, $widg, $color) = @_;
  my $h = { widg => $widg, color => $color};
  bless  $h, $classnm;
}

sub PRINT {
  my $me = shift;
  my $color = $me->{color};
  my $printed = join $/,  @_;
  $printed =~ s/</&lt;/gs;
  $printed =~ s/>/&gt;/gs;
  $printed =~ s/\n/<br>/gs;
  $me->{widg}->setText( $me->{widg}->text . "<font color=\"$color\">$printed</font>" );
}

#tie *STDOUT, 'Qt::TextHandle', $shw->sessionLog, 'black';
#tie *STDERR, 'Qt::TextHandle', $shw->sessionLog, 'red';

=end

def evalOneLine(line)
    prot = ln = line
    prot.gsub('<', '&lt;') # /gs ???
    prot.gsub('>', '&gt;') # /gs ???

    logAppend( "#{@prompt}#{prot}" )

    ostdout, ostderr = $stdout, $stderr
    strio = StringIO.new
    $stdout, $stderr = strio, strio
    begin
        if line =~ /^\$(.*)$/
            eval $1, bind 
        else
            eval line
        end
    rescue StandardError, ScriptError => e
        prot = e.to_str
        prot.gsub(/</, '&lt;') # /gs ???
        prot.gsub(/>/, '&gt;') # /gs ???
        c = @shellWindow.sessionLog.color
        prot.gsub(/\n/, '<br>') # /gs ???
        logAppend '<font color="red">' << prot << '</font><br>'
        shellWindow.sessionLog.setColor c
    end
    logAppend '<font color="blue">' << strio.string << '</font><br>'
    @shellWindow.executedLines << line
    @shellWindow.comboBox.clearEdit
    @shellWindow.comboBox.setFocus
    $stdout, $stderr = ostdout, ostderr

    @shellWindow.sessionLog.scrollToBottom
end

def evalFile(fname)
    if fname == "__DATA__"
        file = StringIO.new
        file.string = $example
    else
        file = File.open(fname, "r")
        if file.nil?
            # TODO use $! -> error message!
            Qt::MessageBox::warning(
                self, 
                "Error" , 
                "Couldn't open @{$fn}: $!", 
                Qt::MessageBox::Ok, 
                Qt::MessageBox::NoButton)
            return
        end
        indata = file
    end
    file.each_line {
        |line| 
        evalOneLine(line)    
    }
    file.close
end

def evalSelection(selection)
	evalOneLine(selection.sub(/.*span>/, "")) unless selection.nil?
end

end

app = Qt::Application.new(ARGV)
w = QtShell.new(nil, "mainWindow")
app.setMainWidget(w.shellWindow)
app.exec

BEGIN {
$example = <<EXAMPLE
$ attr_accessor :datetime, :button, :textedit, :sample, :vbox
statusBar.message("Hello World !")
@vbox = VBox.new(self)
@datetime = DateTimeEdit.new(vbox)
@textedit = TextEdit.new(vbox)
@button = PushButton.new("Hello World!", vbox)
self.setCentralWidget(vbox)
self.resize(220,240)
@vbox.show
@sample = Qt::PopupMenu.new(self)
$ slots 'there()'
@sample.insertItem("&There", self, SLOT('there()'))
self.menuBar.insertItem("&Here", sample)
$ def there; statusBar.message("There...", 2000); end
EXAMPLE
}
