#!/usr/bin/env ruby # rbqtsh : a graphical shell for qtruby. # # author: Germain Garand # ruby port: Alexander Kellett # license: GNU Public License v2 # require 'stringio' case File.basename $0 when "rbqtsh" require 'Qt' when "rbkdesh" 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 = '$>' self.setCaption("MainWindow - this") @shellWindow.sessionLog.setText("Ready.
") 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//>/gs; $printed =~ s/\n/
/gs; $me->{widg}->setText( $me->{widg}->text . "$printed" ); } #tie *STDOUT, 'Qt::TextHandle', $shw->sessionLog, 'black'; #tie *STDERR, 'Qt::TextHandle', $shw->sessionLog, 'red'; =end def evalOneLine(line) prot = ln = line prot.gsub('<', '<') # /gs ??? prot.gsub('>', '>') # /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(//, '>') # /gs ??? c = @shellWindow.sessionLog.color prot.gsub(/\n/, '
') # /gs ??? logAppend '' << prot << '
' shellWindow.sessionLog.setColor c end logAppend '' << strio.string << '
' @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 = <