Exemplo n.º 1
0
    def __init__(self, thegame):
        self.timer_active = False
        self.timer_id = -1
        self.thegame = thegame
        self.me = MidEnd(self, thegame)
        self.me.new_game() # 2174

        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_title(thegame.name)

        vbox = gtk.VBox()
        self.window.add(vbox)
        vbox.show()

        self.accelgroup = gtk.AccelGroup()
        self.window.add_accel_group(self.accelgroup)

        hbox = gtk.HBox()
        vbox.pack_start(hbox, False, False)

        self.menubar = self.add_menubar(hbox)

        self.changed_preset()

        self.snaffle_colours()

        colmap = gtk.gdk.colormap_get_system()
        bg = [x * 65535 for x in self.colours[0] ]
        self.background = gtk.gdk.Color(bg[0], bg[1], bg[2])
        colmap.alloc_color(self.background, False, False)

        if self.me.wants_statusbar():
            viewport = gtk.Viewport()
            viewport.set_shadow_type(gtk.SHADOW_NONE)
            self.statusbar = gtk.Statusbar()
            viewport.add(self.statusbar)
            viewport.show()
            vbox.pack_end(viewport, False, False)
            self.statusbar.show()
            self.statusctx = self.statusbar.get_context_id('game')
            self.statusbar.push(self.statusctx, 'test')
            x, y = self.statusbar.size_request()
            viewport.set_size_request(x, y)
        else:
            self.statusbar = None
            self.statusctx = None

        self.area = gtk.DrawingArea()
        self.area.set_double_buffered(False)
        x, y = self.get_size()
        self.drawing_area_shrink_pending = False
        self.area.set_size_request(x, y)
        self.w = x
        self.h = y

        vbox.pack_end(self.area, True, True, 0)

        self.clear_backing_store()
        self.fonts = None
        self.nfonts = 0
        self.fontsize = 0

        self.paste_data = None

        self.window.connect('destroy', self.destroy);
        self.window.connect('key-press-event', self.key_event);
        self.area.connect('button-press-event', self.button_event);
        self.area.connect('button-release-event', self.button_event);
        self.area.connect('motion-notify-event', self.motion_event);
        self.area.connect('selection-get', self.selection_get);
        self.area.connect('selection-clear-event', self.selection_clear);
        self.area.connect('expose-event', self.expose_area);
        self.window.connect('map-event', self.map_window);
        self.area.connect('configure-event', self.configure_area);
        self.window.connect('configure-event', self.configure_window);


        self.window.connect('delete-event', self.destroy)
        self.window.show_all()
        self.set_window_background(0)
Exemplo n.º 2
0
class FrontEnd(object):

    def __init__(self, thegame):
        self.timer_active = False
        self.timer_id = -1
        self.thegame = thegame
        self.me = MidEnd(self, thegame)
        self.me.new_game() # 2174

        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_title(thegame.name)

        vbox = gtk.VBox()
        self.window.add(vbox)
        vbox.show()

        self.accelgroup = gtk.AccelGroup()
        self.window.add_accel_group(self.accelgroup)

        hbox = gtk.HBox()
        vbox.pack_start(hbox, False, False)

        self.menubar = self.add_menubar(hbox)

        self.changed_preset()

        self.snaffle_colours()

        colmap = gtk.gdk.colormap_get_system()
        bg = [x * 65535 for x in self.colours[0] ]
        self.background = gtk.gdk.Color(bg[0], bg[1], bg[2])
        colmap.alloc_color(self.background, False, False)

        if self.me.wants_statusbar():
            viewport = gtk.Viewport()
            viewport.set_shadow_type(gtk.SHADOW_NONE)
            self.statusbar = gtk.Statusbar()
            viewport.add(self.statusbar)
            viewport.show()
            vbox.pack_end(viewport, False, False)
            self.statusbar.show()
            self.statusctx = self.statusbar.get_context_id('game')
            self.statusbar.push(self.statusctx, 'test')
            x, y = self.statusbar.size_request()
            viewport.set_size_request(x, y)
        else:
            self.statusbar = None
            self.statusctx = None

        self.area = gtk.DrawingArea()
        self.area.set_double_buffered(False)
        x, y = self.get_size()
        self.drawing_area_shrink_pending = False
        self.area.set_size_request(x, y)
        self.w = x
        self.h = y

        vbox.pack_end(self.area, True, True, 0)

        self.clear_backing_store()
        self.fonts = None
        self.nfonts = 0
        self.fontsize = 0

        self.paste_data = None

        self.window.connect('destroy', self.destroy);
        self.window.connect('key-press-event', self.key_event);
        self.area.connect('button-press-event', self.button_event);
        self.area.connect('button-release-event', self.button_event);
        self.area.connect('motion-notify-event', self.motion_event);
        self.area.connect('selection-get', self.selection_get);
        self.area.connect('selection-clear-event', self.selection_clear);
        self.area.connect('expose-event', self.expose_area);
        self.window.connect('map-event', self.map_window);
        self.area.connect('configure-event', self.configure_area);
        self.window.connect('configure-event', self.configure_window);


        self.window.connect('delete-event', self.destroy)
        self.window.show_all()
        self.set_window_background(0)

    def destroy(self, widget):
        self.me.free()
        gtk.main_quit()

    def key_event(self, widget, event):
        shift = MOD_SHIFT if event.state & gtk.gdk.SHIFT_MASK else 0
        ctrl = MOD_CTRL if event.state & gtk.gdk.CONTROL_MASK else 0
        if not self.backing_store_ok():
            return True
        if self.window.activate_key(event):
            return True

        keyval = -1
        if event.keyval == gtk.keysyms.Up:
            keyval = shift | ctrl | CURSOR_UP
        elif event.keyval == gtk.keysyms.KP_Up or event.keyval == gtk.keysyms.KP_8:
            keyval = MOD_NUM_KEYPAD | '8'
        elif event.keyval == gtk.keysyms.Down:
            keyval = shift | ctrl | CURSOR_DOWN
        elif event.keyval == gtk.keysyms.KP_Down or event.keyval == gtk.keysyms.KP_2:
            keyval = MOD_NUM_KEYPAD | '2'
        elif event.keyval == gtk.keysyms.Left:
            keyval = shift | ctrl | CURSOR_LEFT
        elif event.keyval == gtk.keysyms.KP_Left or event.keyval == gtk.keysyms.KP_4:
            keyval = MOD_NUM_KEYPAD | '4'
        elif event.keyval == gtk.keysyms.Right:
            keyval = shift | ctrl | CURSOR_RIGHT
        elif event.keyval == gtk.keysyms.KP_Right or event.keyval == gtk.keysyms.KP_6:
            keyval = MOD_NUM_KEYPAD | '6'
        elif event.keyval == gtk.keysyms.KP_Home or event.keyval == gtk.keysyms.KP_7:
            keyval = MOD_NUM_KEYPAD | '7'
        elif event.keyval == gtk.keysyms.KP_End or event.keyval == gtk.keysyms.KP_1:
            keyval = MOD_NUM_KEYPAD | '1'
        elif event.keyval == gtk.keysyms.KP_Page_Up or event.keyval == gtk.keysyms.KP_9:
            keyval = MOD_NUM_KEYPAD | '9'
        elif event.keyval == gtk.keysyms.KP_Page_Down or event.keyval == gtk.keysyms.KP_3:
            keyval = MOD_NUM_KEYPAD | '3'
        elif event.keyval == gtk.keysyms.KP_Insert or event.keyval == gtk.keysyms.KP_0:
            keyval = MOD_NUM_KEYPAD | '0'
        elif event.keyval == gtk.keysyms.KP_Begin or event.keyval == gtk.keysyms.KP_5:
            keyval = MOD_NUM_KEYPAD | '5'
        elif event.keyval == gtk.keysyms.BackSpace or \
             event.keyval == gtk.keysyms.Delete or \
             event.keyval == gtk.keysyms.KP_Delete:
            keyval = 0177
        elif len(event.string) == 1:
            keyval = ord(event.string[0])
        else:
            keyval = -1

        if keyval >= 0 and not self.me.process_key(0, 0, keyval):
            self.window.destroy()
        return True

    def button_event(self, widget, event):
        if not self.backing_store_ok():
            return True

        if event.type != gtk.gdk.BUTTON_PRESS and \
           event.type != gtk.gdk.BUTTON_RELEASE:
            return True

        button = 0
        if event.button == 2 or (event.state & gtk.gdk.SHIFT_MASK):
            button = MIDDLE_BUTTON
        elif event.button == 3 or (event.state & gtk.gdk.MOD1_MASK):
            button = RIGHT_BUTTON
        elif event.button == 1:
            button = LEFT_BUTTON
        else:
            return False # Could not determine what button!

        if event.type == gtk.gdk.BUTTON_RELEASE:
            button += LEFT_RELEAE - LEFT_BUTTON

        if not self.me.process_key(event.x - self.ox,
                                   event.y - self.oy, button):
            self.window.destroy()
        return True

    def clear_backing_store(self):
        self.image = None

    def motion_event(self, widget, event):
        if not self.backing_store_ok():
            return True
        button = 0
        if event.state & (gtk.gdk.BUTTON2_MASK | gtk.gdk.SHIFT_MASK):
            button = MIDDLE_DRAG
        elif event.state & gtk.gdk.BUTTON1_MASK:
            button = LEFT_DRAG
        elif event.state & gtk.gdk.BUTTON3_MASK:
            button = RIGHT_DRAG
        else:
            return False

        if not self.me.process_key(event.x - self.ox,
                                   event.y - self.oy, button):
            self.window.destroy()
        gtk.gdk.event_request_motions(event)
        return True

    def expose_area(self, widget, event):
        if self.backing_store_ok():
            self.repaint_rectangle(widget, event.area.x, event.area.y,
                                   event.area.width, event.area.height)
        return True

    def map_window(self, widget, event):
        self.window.queue_draw()
        return True

    def configure_area(self, widget, event):
        if self.backing_store_ok():
            self.teardown_backing_store()

        x = self.w = event.width
        y = self.h = event.height
        x, y = self.me.size(x, y, True)
        self.pw = x
        self.ph = y
        self.ox = (self.w - self.pw) / 2
        self.oy = (self.h - self.ph) / 2

        self.setup_backing_store()
        self.me.force_redraw()

        return True

    def configure_window(self, widget, event):
        return True

    def selection_get(self, widget, seldata, info, time_stamp):
        seldata.set(seldata.target, 8, self.paste_data)

    def selection_clear(self, widget, event):
        self.paste_data = None
        return True

    def backing_store_ok(self):
        return self.image != None

    def teardown_backing_store(self):
        self.image = None
        self.pixmap = None

    def setup_backing_store(self):
        self.pixmap = gtk.gdk.Pixmap(self.area.window, self.pw, self.ph)
        self.image = cairo.ImageSurface(cairo.FORMAT_RGB24, self.pw, self.ph)

        def color_paint(cr):
            cr.set_source_rgb(self.colours[0][0],
                    self.colours[0][1],
                    self.colours[0][2])
            cr.paint()
        cr = cairo.Context(self.image) ; color_paint(cr)
        cr = self.pixmap.cairo_create() ; color_paint(cr)
        cr = self.area.window.cairo_create() ; color_paint(cr)

    def repaint_rectangle(self, widget, x, y, w, h):
        gc = gtk.gdk.GC(widget.window)
        gc.set_foreground(self.background)
        if x < self.ox:
            widget.window.draw_rectangle(gc, True,
                    x, y, self.ox - x, h)
            w -= (self.ox - x)
            x = self.ox
        if y < self.oy:
            widget.window.draw_rectangle(gc,
                    True, x, y, w, self.oy - y)
            h -= (self.oy - y)
            y = self.oy
        if w > self.pw:
            widget.window.draw_rectangle(gc,
                    True, x + self.pw, y, w - self.pw, h)
            w = self.pw
        if h > self.ph:
            widget.window.draw_rectangle(gc,
                    True, x, y + self.ph, w, h - self.ph)
        widget.window.draw_drawable(gc, self.pixmap,
                x - self.ox, y - self.oy, x, y, w, h)

    def status_bar(self, text):
        assert(self.statusbar)
        self.statusbar.pop(self.statusctx)
        self.statusbar.push(self.statusctx, text)

    def setup_drawing(self):
        self.cr = cairo.Context(self.image)
        self.cr.set_antialias(cairo.ANTIALIAS_GRAY)
        self.cr.set_line_width(1.0)
        self.cr.set_line_cap(cairo.LINE_CAP_SQUARE)
        self.cr.set_line_join(cairo.LINE_JOIN_ROUND)

    def teardown_drawing(self):
        self.cr = None
        cr = self.pixmap.cairo_create()
        cr.set_source_surface(self.image, 0, 0)
        cr.rectangle(self.bbox_l - 1, self.bbox_u - 1,
                self.bbox_r - self.bbox_l + 2,
                self.bbox_d - self.bbox_u + 2)
        cr.fill()

    def start_draw(self):
        self.bbox_l = self.w
        self.bbox_r = 0
        self.bbox_u = self.h
        self.bbox_d = 0
        self.setup_drawing()

    def clip(self, x, y, w, h):
        self.cr.new_path()
        self.cr.rectangle(x, y, w, h)
        self.cr.clip()

    def unclip(self):
        self.cr.reset_clip()

    def draw_rect(self, x, y, w, h, colour):
        self.set_colour(colour)
        self.cr.save()
        self.cr.new_path()
        self.cr.set_antialias(cairo.ANTIALIAS_NONE)
        self.cr.rectangle(x, y, w, h)
        self.cr.fill()
        self.cr.restore()

    def draw_line(self, x1, y1, x2, y2, colour):
        self.set_colour(colour)
        self.dr.new_path()
        self.cr.move_to(x1 + 0.5, y1 + 0.5)
        self.cr.line_to(x2 + 0.5, y2 + 0.5)
        self.cr.stroke()

    def draw_polygon(self, coords, fillcolour, outlinecolour):
        self.cr.new_path()
        for i in range(0, len(coords)/2, 2):
            self.cr.line_to(coords[i] + 0.5, coords[i+1] + 0.5)
        self.cr.close_path()
        if fillcolour >= 0:
            self.set_colour(fillcolour)
            self.cr.fill_preserve()
        assert(outlinecolour >= 0)
        self.set_colour(outlinecolour)
        self.cr.stroke()


    def draw_update(self, x, y, w, h):
        if self.bbox_l > x:   self.bbox_l = x
        if self.bbox_r < x+w: self.bbox_r = x+w
        if self.bbox_u > y:   self.bbox_u = y
        if self.bbox_d < y+h: self.bbox_d = y+h

    def draw_update(self, x, y, w, h):
        if (self.bbox_l > x  ): self.bbox_l = x
        if (self.bbox_r < x+w): self.bbox_r = x+w
        if (self.bbox_u > y  ): self.bbox_u = y
        if (self.bbox_d < y+h): self.bbox_d = y+h

    def end_draw(self):
        self.teardown_drawing()

        if self.bbox_l < self.bbox_r and self.bbox_u < self.bbox_d:
            self.repaint_rectangle(self.area,
                    self.bbox_l - 1 + self.ox,
                    self.bbox_u - 1 + self.oy,
                    self.bbox_r - self.bbox_l + 2,
                    self.bbox_d - self.bbox_u + 2)

    def get_size(self):
        x, y = self.me.size(sys.maxint, sys.maxint, False)
        return (x, y)

    def snaffle_colours(self):
        self.colours, self.ncolours = self.me.colours()

    def set_colour(self, colour):
        self.cr.set_source_rgb(
                self.colours[colour][0],
                self.colours[colour][1],
                self.colours[colour][2])

    def default_colour(self):
        col = self.window.style.bg[gtk.STATE_NORMAL]
        return (col.red / 65535.0, col.green / 65565.0, col.blue / 65535.0)

    def set_window_background(self, colour):
        colmap = gtk.gdk.colormap_get_system()
        bg = [x * 65535 for x in self.colours[colour] ]
        self.background = gtk.gdk.Color(bg[0], bg[1], bg[2])
        colmap.alloc_color(self.background, False, False)
        self.area.window.set_background(self.background)
        self.window.window.set_background(self.background)
        #self.window.modify_bg(gtk.STATE_NORMAL, self.background)

    def add_menubar(self, container):
        menubar = gtk.MenuBar()
        container.pack_start(menubar)

        menuitem = gtk.MenuItem('_Game')
        menubar.add(menuitem)

        menu = gtk.Menu()
        menuitem.set_submenu(menu)

        self.add_menu_item_with_key(menu, '_New', 'n')

        menuitem = gtk.MenuItem('Restart')
        menu.add(menuitem)
        menuitem.connect('activate', self.menu_restart_event)

        menuitem = gtk.MenuItem('Specific...')
        menuitem.set_data('user-data', CFG_DESC)
        menu.add(menuitem)
        menuitem.connect('activate', self.menu_config_event)

        menuitem = gtk.MenuItem('Random Seed...')
        menuitem.set_data('user-data', CFG_SEED)
        menu.add(menuitem)
        menuitem.connect('activate', self.menu_config_event)

        self.preset_radio = None
        self.preset_custom = None
        self.n_preset_menu_items = 0
        self.preset_threaded = False

        n = self.me.num_presets()
        if n > 0 or self.thegame.can_configure:
            print "FE: got {} presets from me".format(n)
            menuitem = gtk.MenuItem('_Type')
            menubar.add(menuitem)

            submenu = gtk.Menu()
            menuitem.set_submenu(submenu)

            for i in range(n):
                params, name = self.me.fetch_preset(i)
                menuitem = gtk.RadioMenuItem(self.preset_radio, name)
                if not self.preset_radio:
                    self.preset_radio = menuitem
                self.n_preset_menu_items += 1
                submenu.add(menuitem)
                menuitem.set_data('user-data', params)
                menuitem.connect('activate', self.menu_preset_event)

            if self.thegame.can_configure:
                menuitem = gtk.RadioMenuItem(self.preset_radio, "Custom...")
                self.preset_custom = menuitem
                self.preset_radio = menuitem.get_group()
                submenu.add(menuitem)
                menuitem.set_data('user-data', CFG_SETTINGS)
                menuitem.connect('activate', self.menu_config_event)

        menu.add(gtk.MenuItem()) # separator
        menuitem = gtk.MenuItem('Load...')
        menu.add(menuitem)
        menuitem.connect('activate', self.menu_load_event)

        menuitem = gtk.MenuItem('Save...')
        menu.add(menuitem)
        menuitem.connect('activate', self.menu_save_event)

        menu.add(gtk.MenuItem()) # separator
        self.add_menu_item_with_key(menu, 'Undo', 'u')
        self.add_menu_item_with_key(menu, 'Redo', 'r')

        if self.thegame.can_solve:
            menu.add(gtk.MenuItem()) # separator
            menuitem = gtk.MenuItem('Solve')
            menu.add(menuitem)
            menuitem.connect('activate', self.menu_solve_event)

        menu.add(gtk.MenuItem()) # separator
        self.add_menu_item_with_key(menu, 'Exit', 'q')

        menuitem = gtk.MenuItem('_Help')
        menubar.add(menuitem)

        menu = gtk.Menu()
        menuitem.set_submenu(menu)

        menuitem = gtk.MenuItem('About')
        menu.add(menuitem)
        menuitem.connect('activate', self.menu_about_event)

        container.show_all()
        return menubar

    def add_menu_item_with_key(self, cont, text, key):
        menuitem = gtk.MenuItem(text)
        cont.add(menuitem)
        menuitem.set_data('user-data', key)
        menuitem.connect('activate', self.menu_key_event)
        accel_mod = 0
        mask = ord(key) & ~0x1F
        if mask == 0x00:
            key += 0x60 + ord(key)
            accel_mod = gtk.gdk.CONTROL_MASK
        elif mask == 0x40:
            key += 0x20
            accel_mod = gtk.gdk.SHIFT_MASK
        menuitem.add_accelerator('activate', self.accelgroup,
                                 ord(key), accel_mod,
                                 gtk.ACCEL_VISIBLE)

    def menu_key_event(self, menuitem):
        print 'FrontEnd.menu_key_event'
        key = menuitem.get_data('user-data')
        print 'key accelerator: {}'.format(key)
        # TODO: call self.me.process_key
        # TODO: conditionally call self.window.destroy() (on q)

    def changed_preset(self):
        n = self.me.which_preset()
        self.preset_threaded = True
        if n > 0 and self.preset_custom:
            self.preset_custom.set_active()
        else:
            gslist = self.preset_radio
            i = self.n_preset_menu_items - 1 - n
            if self.preset_custom:
                gslist = gslist[1:]
            if i < len(gslist):
                gslist[i].set_active(True)
            else:
                for item in self.preset_radio:
                    item.set_active(False)
        self.preset_threaded = False

    def menu_solve_event(self, menuitem):
        print 'FrontEnd.menu_solve_event'

    def menu_restart_event(self, menuitem):
        print 'FrontEnd.menu_restart_event:'
        self.me.restart_game()

    def menu_config_event(self, menuitem):
        print 'FrontEnd.menu_config_event:'
        which = menuitem.get_data('user-data')

        if self.preset_threaded:
            # TODO: || (check-menu-item !active)
            return
        # TODO: self.changed_preset
        # TODO: self.get_config
        self.me.new_game()
        # TODO: self.resize

    def menu_load_event(self, menuitem):
        print 'FrontEnd.menu_load_event:'
        # TODO: implement

    def menu_save_event(self, menuitem):
        print 'FrontEnd.menu_save_event:'
        # TODO: implement

    def menu_about_event(self, menuitem):
        title = 'About {}'.format(self.thegame.name)
        textbuf = '''{}

from Simon Tatham's Portable Puzzle Collection

{}'''.format(self.thegame.name, VER)
        self.message_box(self.window, title, textbuf)

    def menu_preset_event(self, menuitem):
        params = menuitem.get_data('user-data')
        if self.preset_threaded or \
                (type(menuitem) == type(gtk.CheckMenuItem()) and \
                not menuitem.get_active()):
            return
        self.me.set_params(params)
        self.me.new_game()
        # self.resize_fe()

    def message_box(self, parent, title, msg, centre=True, type=MB_OK):
        window = gtk.Dialog(title, parent, gtk.DIALOG_MODAL)
        text = gtk.Label(msg)
        text.set_alignment(0.0, 0.0)
        hbox = gtk.HBox()
        hbox.pack_start(text, False, False, 20)
        window.vbox.pack_start(hbox, False, False, 20)
        window.set_title(title)
        text.set_line_wrap(True)

        if type == MB_OK:
            titles = { 'stock':[ gtk.STOCK_OK ],
                       'def':0,
                       'cancel':0 }
        else:
            titles = { 'stock':[ gtk.STOCK_NO, gtk.STOCK_YES ],
                       'def':1,
                       'cancel':0 }
        i = 0
        for item in titles['stock']:
            button = gtk.Button(None, item, False)
            window.action_area.pack_end(button, False, False, 0)
            if i == titles['def']:
                button.set_flags(gtk.CAN_DEFAULT)
                window.set_default(button)
            if i == titles['cancel']:
                window.connect('key-press-event', self.win_key_press, button)
            button.connect('clicked', self.msgbox_button_clicked, window)
            button.set_data('user-data', i)
            i += 1
        window.connect('destroy', gtk.main_quit)
        window.show_all()
        self.i = -1
        gtk.main()
        if type == MB_OK:
            rc = True
        else:
            rc = self.i == 1
        return rc

    def win_key_press(self, window, event, button):
        if event.keyval == gtk.keysyms.Escape:
            button.emit('clicked')
            return True
        return False

    def msgbox_button_clicked(self, button, window):
        self.i = button.get_data('user-data')
        window.destroy()

    def main(self):
        gtk.main()