#!/usr/bin/env ruby -w

require 'Qt'

class AnimatedThingy < Qt::Label
    attr_accessor :label, :step
    attr_accessor :ox0, :oy0, :ox1, :oy1
    attr_accessor :x0,  :y0,  :x1,  :y1
    attr_accessor :dx0, :dx1, :dy0, :dy1
    NQIX = 10

    def initialize(*k)
        super(*k)
        @label = k[1] + "\n... and wasting CPU\nwith this animation!\n"
        @step = 0
        @ox0, @oy0, @ox1, @oy1 = *Array.new(4) { Array.new(10, 0) }
        @x0 = @y0 = @x1 = @y1 = 0
        @dx0 = rand(8)+2
        @dy0 = rand(8)+2
        @dx1 = rand(8)+2
        @dy1 = rand(8)+2
    end

    def show
        startTimer(100) unless isVisible
        super
    end

    def hide
	super
	killTimers()
    end

    def sizeHint
	Qt::Size.new(120,100)
    end

    def inc(x, dx, b)
	x += dx
	if x < 0
            x = 0
            dx = rand(8) + 2
        elsif x >= b
            x = b-1
            dx = -(rand(8)+2) 
        end
	yield x, dx
    end

    def timerEvent(e)
	p = Qt::Painter.new(self)
	pn = p.pen
	pn.setWidth(2)
	pn.setColor(backgroundColor)
	p.setPen(pn)

	@step = (@step + 1) % NQIX

	p.drawLine(@ox0[@step], @oy0[@step], @ox1[@step], @oy1[@step])

	inc(@x0, @dx0, width)  { |x,dx| @x0, @dx0 = x, dx }
	inc(@y0, @dy0, height) { |y,dy| @y0, @dy0 = y, dy }
	inc(@x1, @dx1, width)  { |x,dx| @x1, @dx1 = x, dx }
	inc(@y1, @dy1, height) { |y,dy| @y1, @dy1 = y, dy }
	@ox0[@step] = @x0
	@oy0[@step] = @y0
	@ox1[@step] = @x1
	@oy1[@step] = @y1

	c = Qt::Color.new
	c.setHsv( (@step*255)/NQIX, 255, 255 ) # rainbow effect
	pn.setColor(c)
	pn.setWidth(2)
	p.setPen(pn)
	p.drawLine(@ox0[@step], @oy0[@step], @ox1[@step], @oy1[@step])
	p.setPen(colorGroup().text())
	p.drawText(rect(), AlignCenter, @label)
	p.end()
    end

	def paintEvent(event)
		p = Qt::Painter.new(self)
		pn = p.pen()
		pn.setWidth(2)
		p.setPen(pn)
		p.setClipRect(event.rect())
		0.upto(NQIX-1) do |i|
			c = Qt::Color.new()
			c.setHsv( (i*255)/NQIX, 255, 255 ) # rainbow effect
			pn.setColor(c)
			p.setPen(pn)
			p.drawLine(@ox0[i], @oy0[i], @ox1[i], @oy1[i])
		end		
		p.setPen(colorGroup().text())
		p.drawText(rect(), AlignCenter, @label)
		p.end
	end
end

class CPUWaster < Qt::Widget
    attr_accessor :menubar, :file, :options, :rects, :pb
    attr_accessor :td_id , :ld_id, :dl_id, :cl_id, :md_id
    attr_accessor :got_stop, :timer_driven, :default_label
    slots 'drawItemRects(int)', 'doMenuItem(int)', 'stopDrawing()', 'timerDriven()'
    slots 'loopDriven()', 'defaultLabel()', 'customLabel()', 'toggleMinimumDuration()'

    FIRST_DRAW_ITEM = 1000
    LAST_DRAW_ITEM  = 1006

    def initialize(*k)
        super(*k)

        @menubar = Qt::MenuBar.new(self, "menu")
        @pb = nil

        @file = Qt::PopupMenu.new
        @menubar.insertItem( "&File", file )
	FIRST_DRAW_ITEM.upto(LAST_DRAW_ITEM) { 
	    |i| file.insertItem(  "#{drawItemRects(i)} Rectangles", i) 
	}
        connect( menubar, SIGNAL('activated(int)'), self, SLOT('doMenuItem(int)') )
        @file.insertSeparator
        @file.insertItem("Quit", $qApp, SLOT('quit()'))
        @options = Qt::PopupMenu.new
        @menubar.insertItem("&Options", options)
        @td_id = options.insertItem("Timer driven",  self, SLOT('timerDriven()'))
        @ld_id = options.insertItem("Loop driven",   self, SLOT('loopDriven()'))
        @options.insertSeparator
        @dl_id = options.insertItem("Default label", self, SLOT('defaultLabel()'))
        @cl_id = options.insertItem("Custom label",  self, SLOT('customLabel()'))
        @options.insertSeparator
        @md_id = options.insertItem("No minimum duration", self, SLOT('toggleMinimumDuration()'))
        @options.setCheckable true

	loopDriven
	defaultLabel

        setFixedSize(400, 300)
        setBackgroundColor(black)
    end

    def drawItemRects(id)
        n = id - FIRST_DRAW_ITEM - 1
        r = 100
	n.downto(0) { |n|
	    r *= (n%3 != 0) ? 5 : 4
	}
        r
    end

    def doMenuItem(id)
        draw drawItemRects(id) if id >= FIRST_DRAW_ITEM && id <= LAST_DRAW_ITEM
    end

    def stopDrawing
        @got_stop = true
    end

    def timerDriven
        @timer_driven = true
        @options.setItemChecked(@td_id, true)
        @options.setItemChecked(@ld_id, false)
    end

    def loopDriven
        @timer_driven = false
        @options.setItemChecked(@td_id, false)
        @options.setItemChecked(@ld_id, true)
    end

    def defaultLabel
        @default_label = true
        @options.setItemChecked(@dl_id, true)
        @options.setItemChecked(@cl_id, false)
    end

    def customLabel
        @default_label = false
        @options.setItemChecked(@dl_id, false)
        @options.setItemChecked(@cl_id, true)
    end

    def toggleMinimumDuration
        checked = @options.isItemChecked(@md_id)
        @options.setItemChecked(@md_id, !checked)
    end

    def timerEvent(e)
        @pb.setProgress( @pb.totalSteps - @rects ) if @rects % 100 == 0
        @rects -= 1

	painter = Qt::Painter.new(self)

	ww = width
	wh = height

	if ww > 8 and wh > 8
	    c = Qt::Color.new(rand(255), rand(255), rand(255))
	    x = rand(ww - 8)
	    y = rand(wh - 8)
	    w = rand(ww - x)
	    h = rand(wh - y)
	    painter.fillRect(x, y, w, h, Qt::Brush.new(c))
	end
	
	painter.end()

        if @rects == 0 || @got_stop
            @pb.setProgress(@pb.totalSteps)
            painter = Qt::Painter.new(self)
            painter.fillRect(0, 0, width(), height(), Qt::Brush.new(backgroundColor))
			painter.end()
            enableDrawingItems(true)
            killTimers()
            @pb = nil
        end
    end

    def newProgressDialog(label, steps, modal)
        d = Qt::ProgressDialog.new(label, "Cancel", steps, self, "progress", modal)
        d.setMinimumDuration(0) if @options.isItemChecked(@md_id)
        d.setLabel( AnimatedThingy.new(d, label) ) unless @default_label
	d.show
        d
    end

    def enableDrawingItems(yes)
        FIRST_DRAW_ITEM.upto(LAST_DRAW_ITEM) {
            |i| menubar.setItemEnabled(i, yes)
        }
    end

    def draw(n)
        if timer_driven
            unless @pb.nil?
                warn("This cannot happen!")
                return
            end
            @rects = n
            @pb = newProgressDialog("Drawing rectangles.\nUsing timer event.", n, false)
            @pb.setCaption("Please Wait")
            connect(@pb, SIGNAL('cancelled()'), self, SLOT('stopDrawing()'))
            enableDrawingItems(false)
            startTimer(0)
            @got_stop = false
        else
            lpb = newProgressDialog("Drawing rectangles.\nUsing loop.", n, true)
            lpb.setCaption("Please Wait")

            painter = Qt::Painter.new(self)
            0.upto(n) { |i|
		if (i % 100) == 0
		    lpb.setProgress(i)
                    break if lpb.wasCancelled
                end
                cw, ch = width, height
                c = Qt::Color.new(rand(255), rand(255), rand(255))
                x = rand(cw - 8)
                y = rand(cw - 8)
                w = rand(cw - x)
                h = rand(cw - y)
                painter.fillRect(x, y, w, h, Qt::Brush.new(c))
            }
            lpb.cancel
            painter.fillRect(0, 0, width, height, Qt::Brush.new(backgroundColor))
			painter.end()
        end
    end
end

a = Qt::Application.new(ARGV)
w = CPUWaster.new
w.show
a.setMainWidget(w)
a.exec
