summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Chabowski <kevin@kch42.de>2013-08-15 20:56:19 +0200
committerKevin Chabowski <kevin@kch42.de>2013-08-15 20:56:19 +0200
commite338efa5d5708d8797fc1bfd30c163415d48f3c7 (patch)
treebcaddb703ac03dad835bf7ec54007946a4d70605
parent8841e007475aaf415cca68047f0fa0d2b3cf7f73 (diff)
downloadbiomed-e338efa5d5708d8797fc1bfd30c163415d48f3c7.tar.gz
biomed-e338efa5d5708d8797fc1bfd30c163415d48f3c7.tar.bz2
biomed-e338efa5d5708d8797fc1bfd30c163415d48f3c7.zip
Added MapWidget
It already draws the map and the biomes and can be moved with the middle mouse button.
-rw-r--r--blockcolors.go144
-rw-r--r--main.go29
-rw-r--r--mapwidget.go321
3 files changed, 491 insertions, 3 deletions
diff --git a/blockcolors.go b/blockcolors.go
new file mode 100644
index 0000000..bac126b
--- /dev/null
+++ b/blockcolors.go
@@ -0,0 +1,144 @@
+package main
+
+import (
+ "github.com/kch42/gomcmap/mcmap"
+ "github.com/mattn/go-gtk/gdk"
+)
+
+var blockColors = map[mcmap.BlockID]*gdk.Color{
+ mcmap.BlkStone: gdk.NewColor("#666666"),
+ mcmap.BlkGrassBlock: gdk.NewColor("#00aa00"),
+ mcmap.BlkDirt: gdk.NewColor("#644804"),
+ mcmap.BlkCobblestone: gdk.NewColor("#7a7a7a"),
+ mcmap.BlkWoodPlanks: gdk.NewColor("#a4721c"),
+ mcmap.BlkBedrock: gdk.NewColor("#111111"),
+ mcmap.BlkWater: gdk.NewColor("#0000ff"),
+ mcmap.BlkStationaryWater: gdk.NewColor("#0000ff"),
+ mcmap.BlkLava: gdk.NewColor("#ff4400"),
+ mcmap.BlkStationaryLava: gdk.NewColor("#ff4400"),
+ mcmap.BlkSand: gdk.NewColor("#f1ee85"),
+ mcmap.BlkGravel: gdk.NewColor("#9ba3a9"),
+ mcmap.BlkGoldOre: gdk.NewColor("#ffa200"),
+ mcmap.BlkIronOre: gdk.NewColor("#e1e1e1"),
+ mcmap.BlkCoalOre: gdk.NewColor("#333333"),
+ mcmap.BlkWood: gdk.NewColor("#a4721c"),
+ mcmap.BlkLeaves: gdk.NewColor("#57a100"),
+ mcmap.BlkGlass: gdk.NewColor("#eeeeff"),
+ mcmap.BlkLapisLazuliOre: gdk.NewColor("#3114e3"),
+ mcmap.BlkLapisLazuliBlock: gdk.NewColor("#3114e3"),
+ mcmap.BlkDispenser: gdk.NewColor("#7a7a7a"),
+ mcmap.BlkSandstone: gdk.NewColor("#f1ee85"),
+ mcmap.BlkNoteBlock: gdk.NewColor("#a4721c"),
+ mcmap.BlkBed: gdk.NewColor("#a00000"),
+ mcmap.BlkPoweredRail: gdk.NewColor("#ff0000"),
+ mcmap.BlkDetectorRail: gdk.NewColor("#ff0000"),
+ mcmap.BlkStickyPiston: gdk.NewColor("#91ba12"),
+ mcmap.BlkCobweb: gdk.NewColor("#dddddd"),
+ mcmap.BlkGrass: gdk.NewColor("#a0f618"),
+ mcmap.BlkPiston: gdk.NewColor("#a4721c"),
+ mcmap.BlkPistonExtension: gdk.NewColor("#a4721c"),
+ mcmap.BlkWool: gdk.NewColor("#ffffff"),
+ mcmap.BlkBlockOfGold: gdk.NewColor("#ffa200"),
+ mcmap.BlkBlockOfIron: gdk.NewColor("#e1e1e1"),
+ mcmap.BlkTNT: gdk.NewColor("#a20022"),
+ mcmap.BlkBookshelf: gdk.NewColor("#a4721c"),
+ mcmap.BlkMossStone: gdk.NewColor("#589b71"),
+ mcmap.BlkObsidian: gdk.NewColor("#111144"),
+ mcmap.BlkTorch: gdk.NewColor("#ffcc00"),
+ mcmap.BlkFire: gdk.NewColor("#ffcc00"),
+ mcmap.BlkMonsterSpawner: gdk.NewColor("#344e6a"),
+ mcmap.BlkOakWoodStairs: gdk.NewColor("#a4721c"),
+ mcmap.BlkChest: gdk.NewColor("#a4721c"),
+ mcmap.BlkRedstoneWire: gdk.NewColor("#ff0000"),
+ mcmap.BlkDiamondOre: gdk.NewColor("#00fff6"),
+ mcmap.BlkBlockOfDiamond: gdk.NewColor("#00fff6"),
+ mcmap.BlkCraftingTable: gdk.NewColor("#a4721c"),
+ mcmap.BlkWheat: gdk.NewColor("#e7ae00"),
+ mcmap.BlkFarmland: gdk.NewColor("#644804"),
+ mcmap.BlkFurnace: gdk.NewColor("#7a7a7a"),
+ mcmap.BlkBurningFurnace: gdk.NewColor("#7a7a7a"),
+ mcmap.BlkSignPost: gdk.NewColor("#a4721c"),
+ mcmap.BlkWoodenDoor: gdk.NewColor("#a4721c"),
+ mcmap.BlkLadders: gdk.NewColor("#a4721c"),
+ mcmap.BlkRail: gdk.NewColor("#dbdbdb"),
+ mcmap.BlkCobblestoneStairs: gdk.NewColor("#7a7a7a"),
+ mcmap.BlkWallSign: gdk.NewColor("#a4721c"),
+ mcmap.BlkLever: gdk.NewColor("#a4721c"),
+ mcmap.BlkStonePressurePlate: gdk.NewColor("#666666"),
+ mcmap.BlkIronDoor: gdk.NewColor("#e1e1e1"),
+ mcmap.BlkWoodenPressurePlate: gdk.NewColor("#a4721c"),
+ mcmap.BlkRedstoneOre: gdk.NewColor("#a00000"),
+ mcmap.BlkGlowingRedstoneOre: gdk.NewColor("#ff0000"),
+ mcmap.BlkRedstoneTorchInactive: gdk.NewColor("#ff0000"),
+ mcmap.BlkRedstoneTorchActive: gdk.NewColor("#ff0000"),
+ mcmap.BlkStoneButton: gdk.NewColor("#666666"),
+ mcmap.BlkSnow: gdk.NewColor("#e5fffe"),
+ mcmap.BlkIce: gdk.NewColor("#9fdcff"),
+ mcmap.BlkSnowBlock: gdk.NewColor("#e5fffe"),
+ mcmap.BlkCactus: gdk.NewColor("#01bc3a"),
+ mcmap.BlkClay: gdk.NewColor("#767a82"),
+ mcmap.BlkSugarCane: gdk.NewColor("#12db50"),
+ mcmap.BlkJukebox: gdk.NewColor("#a4721c"),
+ mcmap.BlkFence: gdk.NewColor("#a4721c"),
+ mcmap.BlkPumpkin: gdk.NewColor("#ff7000"),
+ mcmap.BlkNetherrack: gdk.NewColor("#851c2d"),
+ mcmap.BlkSoulSand: gdk.NewColor("#796a59"),
+ mcmap.BlkGlowstone: gdk.NewColor("#ffff00"),
+ mcmap.BlkNetherPortal: gdk.NewColor("#ff00ff"),
+ mcmap.BlkJackOLantern: gdk.NewColor("#ff7000"),
+ mcmap.BlkRedstoneRepeaterInactive: gdk.NewColor("#ff0000"),
+ mcmap.BlkRedstoneRepeaterActive: gdk.NewColor("#ff0000"),
+ mcmap.BlkTrapdoor: gdk.NewColor("#a4721c"),
+ mcmap.BlkStoneBricks: gdk.NewColor("#666666"),
+ mcmap.BlkHugeBrownMushroom: gdk.NewColor("#b07859"),
+ mcmap.BlkHugeRedMushroom: gdk.NewColor("#dd0000"),
+ mcmap.BlkIronBars: gdk.NewColor("#e1e1e1"),
+ mcmap.BlkGlassPane: gdk.NewColor("#eeeeff"),
+ mcmap.BlkMelon: gdk.NewColor("#9ac615"),
+ mcmap.BlkVines: gdk.NewColor("#50720d"),
+ mcmap.BlkFenceGate: gdk.NewColor("#a4721c"),
+ mcmap.BlkBrickStairs: gdk.NewColor("#c42500"),
+ mcmap.BlkStoneBrickStairs: gdk.NewColor("#666666"),
+ mcmap.BlkMycelium: gdk.NewColor("#7c668c"),
+ mcmap.BlkLilyPad: gdk.NewColor("#50720d"),
+ mcmap.BlkNetherBrick: gdk.NewColor("#c42500"),
+ mcmap.BlkNetherBrickFence: gdk.NewColor("#c42500"),
+ mcmap.BlkNetherBrickStairs: gdk.NewColor("#c42500"),
+ mcmap.BlkEnchantmentTable: gdk.NewColor("#222244"),
+ mcmap.BlkBrewingStand: gdk.NewColor("#666666"),
+ mcmap.BlkCauldron: gdk.NewColor("#666666"),
+ mcmap.BlkEndPortal: gdk.NewColor("#000000"),
+ mcmap.BlkEndPortalBlock: gdk.NewColor("#e0dbce"),
+ mcmap.BlkEndStone: gdk.NewColor("#e0dbce"),
+ mcmap.BlkRedstoneLampInactive: gdk.NewColor("#ffff00"),
+ mcmap.BlkRedstoneLampActive: gdk.NewColor("#ffff00"),
+ mcmap.BlkSandstoneStairs: gdk.NewColor("#f1ee85"),
+ mcmap.BlkEmeraldOre: gdk.NewColor("#00c140"),
+ mcmap.BlkEnderChest: gdk.NewColor("#222244"),
+ mcmap.BlkBlockOfEmerald: gdk.NewColor("#00c140"),
+ mcmap.BlkSpruceWoodStairs: gdk.NewColor("#a4721c"),
+ mcmap.BlkBirchWoodStairs: gdk.NewColor("#a4721c"),
+ mcmap.BlkJungleWoodStairs: gdk.NewColor("#a4721c"),
+ mcmap.BlkCommandBlock: gdk.NewColor("#e8ec78"),
+ mcmap.BlkBeacon: gdk.NewColor("#00fff6"),
+ mcmap.BlkCobblestoneWall: gdk.NewColor("#7a7a7a"),
+ mcmap.BlkCarrots: gdk.NewColor("#ff6000"),
+ mcmap.BlkPotatoes: gdk.NewColor("#c6cd0c"),
+ mcmap.BlkWoodenButton: gdk.NewColor("#a4721c"),
+ mcmap.BlkAnvil: gdk.NewColor("#444444"),
+ mcmap.BlkTrappedChest: gdk.NewColor("#a4721c"),
+ mcmap.BlkRedstoneComparatorInactive: gdk.NewColor("#ff0000"),
+ mcmap.BlkRedstoneComparatorActive: gdk.NewColor("#ff0000"),
+ mcmap.BlkBlockOfRedstone: gdk.NewColor("#ff0000"),
+ mcmap.BlkNetherQuartzOre: gdk.NewColor("#e7e7e7"),
+ mcmap.BlkHopper: gdk.NewColor("#444444"),
+ mcmap.BlkBlockOfQuartz: gdk.NewColor("#e7e7e7"),
+ mcmap.BlkQuartzStairs: gdk.NewColor("#e7e7e7"),
+ mcmap.BlkActivatorRail: gdk.NewColor("#ff0000"),
+ mcmap.BlkDropper: gdk.NewColor("#444444"),
+ mcmap.BlkStainedClay: gdk.NewColor("#767a82"),
+ mcmap.BlkHayBlock: gdk.NewColor("#e7ae00"),
+ mcmap.BlkCarpet: gdk.NewColor("#ffffff"),
+ mcmap.BlkHardenedClay: gdk.NewColor("#767a82"),
+ mcmap.BlkBlockOfCoal: gdk.NewColor("#333333"),
+}
diff --git a/main.go b/main.go
index 9d432db..4ea998d 100644
--- a/main.go
+++ b/main.go
@@ -3,8 +3,10 @@ package main
import (
"fmt"
"github.com/kch42/gomcmap/mcmap"
+ "github.com/mattn/go-gtk/gdk"
"github.com/mattn/go-gtk/glib"
"github.com/mattn/go-gtk/gtk"
+ "os"
)
type GUI struct {
@@ -12,6 +14,8 @@ type GUI struct {
statusbar *gtk.Statusbar
showbiomes *gtk.CheckButton
+ mapw *MapWidget
+
tool Tool
}
@@ -24,7 +28,14 @@ func (g *GUI) openWorldDlg() {
}
func (g *GUI) openWorld(path string) {
- fmt.Println(path)
+ region, err := mcmap.OpenRegion(path, false)
+ if err != nil {
+ dlg := gtk.NewMessageDialog(g.window, gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, "Could not load world %s:\n%s", path, err.Error())
+ dlg.Run()
+ dlg.Destroy()
+ }
+
+ go g.mapw.setRegion(region)
}
func (g *GUI) aboutDlg() {
@@ -185,7 +196,8 @@ func (g *GUI) Init() {
hbox := gtk.NewHBox(false, 0)
- // TODO: Drawing area thing missing...
+ g.mapw = NewMapWidget(g.reportError)
+ hbox.PackStart(g.mapw.DArea(), true, true, 3)
toolbox := g.mkToolbox()
hbox.PackEnd(toolbox, false, false, 3)
@@ -201,6 +213,13 @@ func (g *GUI) Init() {
g.window.Connect("destroy", g.exitApp)
}
+func (g *GUI) reportError(msg string) {
+ dlg := gtk.NewMessageDialog(g.window, gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
+ dlg.Run()
+ dlg.Destroy()
+ os.Exit(1)
+}
+
func (g *GUI) mkUpdateToolFx(rb *gtk.RadioButton, t Tool) func() {
return func() {
if rb.GetActive() {
@@ -226,7 +245,7 @@ func (g *GUI) setBiome(bio mcmap.Biome) {
}
func (g *GUI) showbiomesToggled() {
- fmt.Printf("Show biomes: %v\n", g.showbiomes.GetActive())
+ g.mapw.SetShowBiomes(g.showbiomes.GetActive())
}
func (g *GUI) undo() {
@@ -242,6 +261,9 @@ func (g *GUI) exitApp() {
}
func main() {
+ glib.ThreadInit(nil)
+ gdk.ThreadsInit()
+ gdk.ThreadsEnter()
gtk.Init(nil)
gui := new(GUI)
@@ -249,4 +271,5 @@ func main() {
gui.Show()
gtk.Main()
+ gdk.ThreadsLeave()
}
diff --git a/mapwidget.go b/mapwidget.go
new file mode 100644
index 0000000..764e41e
--- /dev/null
+++ b/mapwidget.go
@@ -0,0 +1,321 @@
+package main
+
+import (
+ "fmt"
+ "github.com/kch42/gomcmap/mcmap"
+ "github.com/mattn/go-gtk/gdk"
+ "github.com/mattn/go-gtk/glib"
+ "github.com/mattn/go-gtk/gtk"
+ "math"
+ "unsafe"
+)
+
+const (
+ zoom = 2
+ tileSize = zoom * mcmap.ChunkSizeXZ
+ halfChunkSize = mcmap.ChunkSizeXZ / 2
+)
+
+type tileCmd int
+
+const (
+ cmdUpdateTiles tileCmd = iota
+ cmdFlushTiles
+ cmdSave
+)
+
+func renderTile(chunk *mcmap.Chunk) (maptile, biotile *gdk.Pixmap) {
+ maptile = emptyPixmap(tileSize, tileSize, 24)
+ mtDrawable := maptile.GetDrawable()
+ mtGC := gdk.NewGC(mtDrawable)
+
+ biotile = emptyPixmap(tileSize, tileSize, 24)
+ btDrawable := biotile.GetDrawable()
+ btGC := gdk.NewGC(btDrawable)
+
+ for z := 0; z < mcmap.ChunkSizeXZ; z++ {
+ scanX:
+ for x := 0; x < mcmap.ChunkSizeXZ; x++ {
+ btGC.SetRgbFgColor(bioColors[chunk.Biome(x, z)])
+ btDrawable.DrawRectangle(btGC, true, x*zoom, z*zoom, zoom, zoom)
+
+ for y := chunk.Height(x, z); y >= 0; y-- {
+ if col, ok := blockColors[chunk.Block(x, y, z).ID]; ok {
+ mtGC.SetRgbFgColor(col)
+ mtDrawable.DrawRectangle(mtGC, true, x*zoom, z*zoom, zoom, zoom)
+ continue scanX
+ }
+ }
+
+ mtGC.SetRgbFgColor(gdk.NewColor("#ffffff"))
+ mtDrawable.DrawRectangle(mtGC, true, x*zoom, z*zoom, zoom, zoom)
+ }
+ }
+
+ return
+}
+
+type MapWidget struct {
+ dArea *gtk.DrawingArea
+ w, h int
+
+ reportFail func(msg string)
+
+ isInit bool
+
+ showBiomes bool
+
+ offX, offZ int
+ mx1, mx2, my1, my2 int
+
+ pixmap *gdk.Pixmap
+ pixmapGC *gdk.GC
+ gdkwin *gdk.Window
+
+ bg *gdk.Pixmap
+
+ region *mcmap.Region
+
+ maptiles map[XZPos]*gdk.Pixmap
+ biotiles map[XZPos]*gdk.Pixmap
+
+ redraw chan bool
+ tileCmds chan tileCmd
+}
+
+var (
+ checker1 = gdk.NewColor("#222222")
+ checker2 = gdk.NewColor("#444444")
+)
+
+func emptyPixmap(w, h, depth int) *gdk.Pixmap {
+ return gdk.NewPixmap(new(gdk.Drawable), w, h, depth)
+}
+
+func (mw *MapWidget) SetShowBiomes(b bool) {
+ mw.showBiomes = b
+ mw.redraw <- true
+}
+
+func (mw *MapWidget) DArea() *gtk.DrawingArea { return mw.dArea }
+
+func (mw *MapWidget) doTileCmds() {
+ for cmd := range mw.tileCmds {
+ switch cmd {
+ case cmdSave:
+ mw.region.Save()
+ case cmdFlushTiles:
+ gdk.ThreadsEnter()
+ for _, mt := range mw.maptiles {
+ mt.Unref()
+ }
+ for _, bt := range mw.biotiles {
+ bt.Unref()
+ }
+ gdk.ThreadsLeave()
+
+ mw.maptiles = make(map[XZPos]*gdk.Pixmap)
+ mw.biotiles = make(map[XZPos]*gdk.Pixmap)
+ case cmdUpdateTiles:
+ todelete := make(map[XZPos]bool)
+
+ startX := int(math.Floor(float64(mw.offX) / tileSize))
+ startZ := int(math.Floor(float64(mw.offZ) / tileSize))
+ endX := int(math.Ceil(float64(mw.offX+mw.w) / tileSize))
+ endZ := int(math.Ceil(float64(mw.offZ+mw.h) / tileSize))
+
+ for pos := range mw.maptiles {
+ if (pos.X < startX) || (pos.Z < startZ) || (pos.X >= endX) || (pos.Z >= endZ) {
+ todelete[pos] = true
+ }
+ }
+
+ gdk.ThreadsEnter()
+ for pos := range todelete {
+ if tile, ok := mw.maptiles[pos]; ok {
+ tile.Unref()
+ delete(mw.maptiles, pos)
+ }
+
+ if tile, ok := mw.biotiles[pos]; ok {
+ tile.Unref()
+ delete(mw.biotiles, pos)
+ }
+ }
+
+ for z := startZ; z < endZ; z++ {
+ scanX:
+ for x := startX; x < endX; x++ {
+ pos := XZPos{x, z}
+
+ if _, ok := mw.biotiles[pos]; ok {
+ continue scanX
+ }
+
+ chunk, err := mw.region.Chunk(x, z)
+ switch err {
+ case nil:
+ case mcmap.NotAvailable:
+ continue scanX
+ default:
+ mw.reportFail(fmt.Sprintf("Could not get chunk %d, %d: %s", x, z, err))
+ return
+ }
+
+ mw.maptiles[pos], mw.biotiles[pos] = renderTile(chunk)
+ chunk.MarkUnused()
+
+ gdk.ThreadsLeave()
+ mw.redraw <- true
+ gdk.ThreadsEnter()
+ }
+ }
+
+ gdk.ThreadsLeave()
+ }
+ }
+}
+
+func (mw *MapWidget) configure() {
+ if mw.pixmap != nil {
+ mw.pixmap.Unref()
+ }
+
+ alloc := mw.dArea.GetAllocation()
+ mw.w = alloc.Width
+ mw.h = alloc.Height
+
+ if !mw.isInit {
+ mw.offX = -(mw.w / 2)
+ mw.offZ = -(mw.h / 2)
+ mw.isInit = true
+ }
+
+ mw.pixmap = gdk.NewPixmap(mw.dArea.GetWindow().GetDrawable(), mw.w, mw.h, 24)
+ mw.pixmapGC = gdk.NewGC(mw.pixmap.GetDrawable())
+
+ mw.drawBg()
+ mw.compose()
+}
+
+func (mw *MapWidget) drawBg() {
+ if mw.bg != nil {
+ mw.bg.Unref()
+ }
+
+ mw.bg = emptyPixmap(mw.w, mw.h, 24)
+ drawable := mw.bg.GetDrawable()
+ gc := gdk.NewGC(drawable)
+
+ w := (mw.w + 16) / 32
+ h := (mw.h + 16) / 32
+
+ for y := 0; y < h; y++ {
+ for x := 0; x < w; x++ {
+ if (x % 2) == (y % 2) {
+ gc.SetRgbFgColor(checker1)
+ } else {
+ gc.SetRgbFgColor(checker2)
+ }
+ drawable.DrawRectangle(gc, true, x*32, y*32, 32, 32)
+ }
+ }
+}
+
+func (mw *MapWidget) compose() {
+ drawable := mw.pixmap.GetDrawable()
+ gc := mw.pixmapGC
+
+ drawable.DrawDrawable(gc, mw.bg.GetDrawable(), 0, 0, 0, 0, -1, -1)
+
+ var tiles map[XZPos]*gdk.Pixmap
+ if mw.showBiomes {
+ tiles = mw.biotiles
+ } else {
+ tiles = mw.maptiles
+ }
+
+ for pos, tile := range tiles {
+ x := (pos.X * tileSize) - mw.offX
+ y := (pos.Z * tileSize) - mw.offZ
+
+ drawable.DrawDrawable(gc, tile.GetDrawable(), 0, 0, x, y, tileSize, tileSize)
+ }
+}
+
+func (mw *MapWidget) movement(ctx *glib.CallbackContext) {
+ if mw.gdkwin == nil {
+ mw.gdkwin = mw.dArea.GetWindow()
+ }
+ arg := ctx.Args(0)
+ mev := *(**gdk.EventMotion)(unsafe.Pointer(&arg))
+ var mt gdk.ModifierType
+ if mev.IsHint != 0 {
+ mw.gdkwin.GetPointer(&(mw.mx2), &(mw.my2), &mt)
+ } else {
+ mw.mx2, mw.my2 = int(mev.X), int(mev.Y)
+ }
+
+ switch {
+ case mt&gdk.BUTTON1_MASK != 0:
+ case mt&gdk.BUTTON2_MASK != 0:
+ if (mw.mx1 != -1) && (mw.my1 != -1) {
+ mw.offX += mw.mx1 - mw.mx2
+ mw.offZ += mw.my1 - mw.my2
+
+ gdk.ThreadsLeave()
+ mw.tileCmds <- cmdUpdateTiles
+ gdk.ThreadsEnter()
+ }
+ }
+
+ mw.mx1, mw.my1 = mw.mx2, mw.my2
+}
+
+func (mw *MapWidget) expose() {
+ mw.dArea.GetWindow().GetDrawable().DrawDrawable(mw.pixmapGC, mw.pixmap.GetDrawable(), 0, 0, 0, 0, -1, -1)
+}
+
+func (mw *MapWidget) guiUpdater() {
+ for _ = range mw.redraw {
+ gdk.ThreadsEnter()
+ mw.compose()
+ mw.expose()
+ mw.dArea.GetWindow().Invalidate(nil, false)
+ gdk.ThreadsLeave()
+ }
+}
+
+func (mw *MapWidget) init() {
+ mw.redraw = make(chan bool)
+ mw.tileCmds = make(chan tileCmd)
+
+ mw.maptiles = make(map[XZPos]*gdk.Pixmap)
+ mw.biotiles = make(map[XZPos]*gdk.Pixmap)
+
+ mw.showBiomes = true
+
+ mw.mx1, mw.my1 = -1, -1
+
+ go mw.doTileCmds()
+ go mw.guiUpdater()
+
+ mw.dArea = gtk.NewDrawingArea()
+ mw.dArea.Connect("configure-event", mw.configure)
+ mw.dArea.Connect("expose-event", mw.expose)
+ mw.dArea.Connect("motion-notify-event", mw.movement)
+
+ mw.dArea.SetEvents(int(gdk.POINTER_MOTION_MASK | gdk.POINTER_MOTION_HINT_MASK | gdk.BUTTON_PRESS_MASK))
+}
+
+func (mw *MapWidget) setRegion(region *mcmap.Region) {
+ mw.tileCmds <- cmdFlushTiles
+ mw.region = region
+ mw.tileCmds <- cmdUpdateTiles
+}
+
+func NewMapWidget(reportFail func(msg string)) *MapWidget {
+ mw := &MapWidget{reportFail: reportFail}
+ mw.init()
+ return mw
+}