4 using System.Collections;
9 public class Application {
11 private Gtk.Window MainWindow;
13 private Gtk.CheckMenuItem useNewSyntax;
15 private Gtk.DrawingArea DrawingArea;
17 private Gtk.CheckButton SolidCheckButton;
19 private Gtk.CheckButton UniSolidCheckButton;
21 private Gtk.CheckButton IceCheckButton;
23 private Gtk.CheckButton WaterCheckButton;
25 private Gtk.CheckButton SlopeCheckButton;
27 private Gtk.CheckButton DontUseCheckButton;
29 private Gtk.CheckButton HiddenCheckButton;
31 private Gtk.Entry DataEntry;
33 private Gtk.Entry AnimFpsEntry;
35 private Gtk.Entry IDEntry;
37 private Gtk.TreeView TileList;
39 private Gtk.Combo TileGroupComboBox;
41 private Gtk.MenuItem AddTileGroupMenu;
44 private Gtk.Dialog RemapDialog;
46 private Gtk.SpinButton RD_spinButton;
48 private string tilesetdir;
49 private string tilesetfile;
50 private TileSet tileset;
51 private TileGroup selectedgroup;
54 private bool[] SelectionArray;
55 private ArrayList Selection = new ArrayList();
58 private bool toggling;
59 private bool selecting;
61 private string currentimage;
62 private Gdk.Pixbuf pixbuf;
64 public static int Main(string[] args) {
65 Gtk.Application.Init();
67 Application app = new Application();
69 /* that's no proper commandlineparsing, but who'll notice... */
71 app.LoadTileSet(args[0]);
73 Gtk.Application.Run();
77 public Application() {
78 Glade.XML gxml = new Glade.XML(null, "tiler.glade", null, null);
79 gxml.Autoconnect(this);
81 if(MainWindow == null || DrawingArea == null || RemapDialog == null)
82 throw new Exception("some widgets not found");
84 DrawingArea.AddEvents((int) Gdk.EventMask.ButtonPressMask);
85 DrawingArea.AddEvents((int) Gdk.EventMask.ButtonReleaseMask);
86 DrawingArea.AddEvents((int) Gdk.EventMask.ButtonMotionMask);
91 protected void OnOpen(object o, EventArgs e) {
92 FileChooserDialog fileChooser = new FileChooserDialog("Select TileSet", MainWindow, FileChooserAction.Open, new object[] {});
94 fileChooser.AddButton(Gtk.Stock.Cancel, Gtk.ResponseType.Cancel);
95 fileChooser.AddButton(Gtk.Stock.Ok, Gtk.ResponseType.Ok);
96 fileChooser.DefaultResponse = Gtk.ResponseType.Ok;
97 Gtk.FileFilter filter;
98 filter = new Gtk.FileFilter();
99 filter.Name = "Supertux tilesets";
100 filter.AddPattern("*.strf");
101 filter.AddPattern("*.stgt");
102 fileChooser.AddFilter( filter );
103 filter = new Gtk.FileFilter();
104 filter.Name = "Supertux 0.1.x tilesets";
105 filter.AddPattern("*.stgt");
106 fileChooser.AddFilter( filter );
107 filter = new Gtk.FileFilter();
108 filter.Name = "Supertux 0.3.x tilesets";
109 filter.AddPattern("*.strf");
110 fileChooser.AddFilter( filter );
111 Gtk.FileFilter all = new Gtk.FileFilter();
112 all.Name = "All Files";
114 fileChooser.AddFilter( all );
115 int result = fileChooser.Run();
118 if(result != (int) ResponseType.Ok)
121 LoadTileSet(fileChooser.Filename);
124 private void LoadTileSet(string file) {
126 tileset = new TileSet();
129 tilesetdir = new FileInfo(file).Directory.ToString();
130 } catch(Exception exception) {
131 ShowException(exception);
136 FillTileGroupComboBox();
139 useNewSyntax.Active = tileset.IsNew;
141 Console.WriteLine("Warning: new syntax of 0.3.x files \"More tiles in one image\" isn't currently supported for WRITING");
145 protected void OnImportImage(object o, EventArgs e) {
146 FileChooserDialog fileChooser = new FileChooserDialog("Select ImageFile", MainWindow, FileChooserAction.Open, new object[] {});
148 fileChooser.AddButton(Gtk.Stock.Cancel, Gtk.ResponseType.Cancel);
149 fileChooser.AddButton(Gtk.Stock.Ok, Gtk.ResponseType.Ok);
150 fileChooser.DefaultResponse = Gtk.ResponseType.Ok;
151 Gtk.FileFilter all = new Gtk.FileFilter();
152 all.Name = "All Files";
154 fileChooser.AddFilter( all );
155 int result = fileChooser.Run();
158 if(result != (int) ResponseType.Ok)
161 string file = fileChooser.Filename;
162 string trim = tilesetdir + "/";
164 if (!file.StartsWith(trim)){
166 "Imported file must be located inside tileset directory");
170 ChangeImage(file.TrimStart(trim.ToCharArray()));
172 int startid = tileset.Tiles.Count;
173 for(int y = 0; y < TilesY; ++y) {
174 for(int x = 0; x < TilesX; ++x) {
176 Tile tile = new Tile();
177 tile.ID = startid + i;
178 ImageRegion region = new ImageRegion();
179 region.ImageFile = currentimage;
180 region.Region = new System.Drawing.Rectangle(x*TileSet.TILE_WIDTH, y*TileSet.TILE_HEIGHT, TileSet.TILE_WIDTH, TileSet.TILE_HEIGHT);
181 tile.Images.Add(region);
182 if(Tiles[i] != null) {
184 "Warning Tile in this region already existed...");
187 tileset.Tiles.Add(tile);
194 private void ChangeImage(string file) {
201 pixbuf = new Pixbuf(tilesetdir + "/" + file);
202 if(pixbuf.Width % TileSet.TILE_WIDTH != 0 || pixbuf.Height % TileSet.TILE_HEIGHT != 0)
203 Console.WriteLine("Warning: Image Width or Height is not a multiple of 32");
204 } catch(Exception e) {
208 currentimage = new FileInfo(file).Name;
209 TilesX = pixbuf.Width / TileSet.TILE_WIDTH;
210 TilesY = pixbuf.Height / TileSet.TILE_HEIGHT;
211 SelectionArray = new bool[TilesX * TilesY];
212 Tiles = new Tile[TilesX * TilesY];
214 // search tileset for tiles with matching image
215 foreach(Tile tile in tileset.Tiles) {
218 if(tile.Images.Count == 0)
220 ImageRegion region = (ImageRegion) tile.Images[0];
221 if(region.ImageFile == currentimage) {
222 int px = region.Region.X / TileSet.TILE_WIDTH;
223 int py = region.Region.Y / TileSet.TILE_HEIGHT;
224 int i = py*TilesX+px;
225 if(i < 0 || i >= Tiles.Length) {
226 Console.WriteLine("Invalid Imageregion at tile " +
230 if(Tiles[i] != null) {
231 Console.WriteLine("Multiple tiles for region " +
232 px*TileSet.TILE_WIDTH + " , " + py*TileSet.TILE_HEIGHT);
239 /* DrawingArea.Allocation
240 = new Gdk.Rectangle(0, 0, pixbuf.Width, pixbuf.Height);*/
241 DrawingArea.WidthRequest = pixbuf.Width;
242 DrawingArea.HeightRequest = pixbuf.Height;
243 DrawingArea.QueueResize();
246 protected void OnSave(object o, EventArgs e) {
247 if (tileset.IsNew && useNewSyntax.Active) {
248 MessageDialog dialog = new MessageDialog(MainWindow, DialogFlags.Modal | DialogFlags.DestroyWithParent, MessageType.Error, ButtonsType.Ok,
249 "Sorry, the file you are editing is too new and 0.3.x syntax is not supported yet.");
253 tileset.Write(tilesetfile);
257 protected void OnQuit(object o, EventArgs e) {
258 Gtk.Application.Quit();
261 protected void OnDeleteQuit(object o, DeleteEventArgs e) {
262 Gtk.Application.Quit();
265 protected void OnAbout(object o, EventArgs e) {
266 // string[] authors = new string[]{
270 Gtk.AboutDialog dialog = new Gtk.AboutDialog();
271 // dialog.Icon = <icon>;
272 dialog.ProgramName = "SuperTux Tiler";
273 dialog.Version = "0.0.3";
274 dialog.Comments = "A tileset editor for SuperTux 0.1.x";
275 // dialog.Authors = authors;
276 dialog.Copyright = "Copyright (c) 2006 SuperTux Devel Team";
278 "This program is free software; you can redistribute it and/or modify" + Environment.NewLine +
279 "it under the terms of the GNU General Public License as published by" + Environment.NewLine +
280 "the Free Software Foundation; either version 2 of the License, or" + Environment.NewLine +
281 "(at your option) any later version." + Environment.NewLine +
282 Environment.NewLine +
283 "This program is distributed in the hope that it will be useful," + Environment.NewLine +
284 "but WITHOUT ANY WARRANTY; without even the implied warranty of" + Environment.NewLine +
285 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the" + Environment.NewLine +
286 "GNU General Public License for more details." + Environment.NewLine +
287 Environment.NewLine +
288 "You should have received a copy of the GNU General Public License" + Environment.NewLine +
289 "along with this program; if not, write to the Free Software Foundation, Inc.," + Environment.NewLine +
290 "59 Temple Place, Suite 330, Boston, MA 02111-1307 USA" + Environment.NewLine;
291 dialog.Website = "http://supertux.lethargik.org/";
292 dialog.WebsiteLabel = "SuperTux on the Web";
297 protected void OnRemapTiles(object o, EventArgs e) {
303 protected void OnRemapDialogCancel(object o, EventArgs e) {
307 protected void OnRemapDialogApply(object o, EventArgs e) {
314 id = RD_spinButton.ValueAsInt;
315 } catch(Exception exception) {
316 ShowException(exception);
321 RD_spinButton.Value = 1;
325 protected void OnCreateTileGroup(object o, EventArgs e) {
328 protected void OnRenameTileGroup(object o, EventArgs e) {
331 protected void RemapTiles(int startID) {
337 foreach(Tile tile in Selection) {
343 // remap in all tilegroups...
344 foreach(TileGroup tilegroup in tileset.TileGroups) {
345 int idx = tilegroup.Tiles.IndexOf(oldid);
347 tilegroup.Tiles[idx] = tile.ID;
355 protected void OnDrawingAreaExpose(object o, ExposeEventArgs e) {
359 Drawable drawable = e.Event.Window;
360 Gdk.GC gc = new Gdk.GC(drawable);
361 drawable.DrawPixbuf(gc, pixbuf, 0, 0, 0, 0,
362 pixbuf.Width, pixbuf.Height, RgbDither.None, 0, 0);
364 gc.RgbFgColor = new Color(0xff, 0, 0);
365 foreach(Tile tile in Selection) {
366 System.Drawing.Rectangle rect
367 = ((ImageRegion) tile.Images[0]).Region;
368 drawable.DrawRectangle(gc, false, rect.X, rect.Y, rect.Width,
375 protected void OnDrawingAreaButtonPress(object o, ButtonPressEventArgs e) {
376 if(SelectionArray == null)
381 for(int i = 0; i < SelectionArray.Length; ++i)
382 SelectionArray[i] = false;
383 select((int) e.Event.X, (int) e.Event.Y);
386 private void select(int x, int y) {
387 int tile = y/TileSet.TILE_HEIGHT * TilesX + x/TileSet.TILE_WIDTH;
388 if(tile < 0 || tile >= SelectionArray.Length)
391 SelectionArray[tile] = true;
392 SelectionArrayChanged();
395 protected void OnDrawingAreaMotionNotify(object i, MotionNotifyEventArgs e) {
398 select((int) e.Event.X, (int) e.Event.Y);
401 protected void OnDrawingAreaButtonRelease(object o, ButtonPressEventArgs e) {
405 protected void OnCheckButtonToggled(object sender, EventArgs e) {
408 foreach(Tile tile in Selection) {
409 if(sender == SolidCheckButton)
410 tile.SetAttribute(Attribute.SOLID, SolidCheckButton.Active);
411 if(sender == UniSolidCheckButton)
412 tile.SetAttribute(Attribute.UNISOLID, UniSolidCheckButton.Active);
413 if(sender == IceCheckButton)
414 tile.SetAttribute(Attribute.ICE, IceCheckButton.Active);
415 if(sender == WaterCheckButton)
416 tile.SetAttribute(Attribute.WATER, WaterCheckButton.Active);
417 if(sender == SlopeCheckButton)
418 tile.SetAttribute(Attribute.SLOPE, SlopeCheckButton.Active);
419 if(sender == HiddenCheckButton)
420 tile.Hidden = HiddenCheckButton.Active;
421 if(sender == DontUseCheckButton)
422 tile.ID = DontUseCheckButton.Active ? -1 : 0;
426 protected void OnEntryChanged(object sender, EventArgs e) {
429 foreach(Tile tile in Selection) {
431 if(sender == IDEntry)
432 tile.ID = Int32.Parse(IDEntry.Text);
433 if(sender == DataEntry)
434 tile.Data = Int32.Parse(DataEntry.Text);
435 if(sender == AnimFpsEntry)
436 tile.AnimFps = Single.Parse(AnimFpsEntry.Text);
438 // ignore parse errors for now...
443 private void SelectionArrayChanged() {
445 for(int i = 0; i < SelectionArray.Length; ++i) {
446 if(!SelectionArray[i])
449 if(Tiles[i] == null) {
450 Console.WriteLine("Tile doesn't exist yet");
451 // TODO ask user to create new tile...
454 Selection.Add(Tiles[i]);
460 private void SelectionChanged() {
463 string nextimage = "";
464 foreach(Tile tile in Selection) {
466 SolidCheckButton.Active = tile.HasAttribute(Attribute.SOLID);
467 UniSolidCheckButton.Active = tile.HasAttribute(Attribute.UNISOLID);
468 IceCheckButton.Active = tile.HasAttribute(Attribute.ICE);
469 WaterCheckButton.Active = tile.HasAttribute(Attribute.WATER);
470 SlopeCheckButton.Active = tile.HasAttribute(Attribute.SLOPE);
471 HiddenCheckButton.Active = tile.Hidden;
472 DontUseCheckButton.Active = tile.ID == -1;
473 DataEntry.Text = tile.Data.ToString();
474 AnimFpsEntry.Text = tile.AnimFps.ToString();
475 IDEntry.Text = tile.ID.ToString();
476 IDEntry.IsEditable = true;
479 if(tile.Images.Count > 0) {
480 nextimage = ((ImageRegion) tile.Images[0]).ImageFile;
483 IDEntry.Text += "," + tile.ID.ToString();
484 IDEntry.IsEditable = false;
485 if(tile.Images.Count > 0
486 && ((ImageRegion) tile.Images[0]).ImageFile != nextimage) {
492 if(nextimage != currentimage)
493 ChangeImage(nextimage);
495 DrawingArea.QueueDraw();
498 private void FillTileList() {
499 TileList.HeadersVisible = true;
500 if(TileList.Columns.Length == 0)
501 TileList.AppendColumn("Tile", new CellRendererText(), "text", 0);
503 ListStore store = new ListStore(typeof(string));
505 if(selectedgroup == null) {
506 foreach(Tile tile in tileset.Tiles) {
509 store.AppendValues(new object[] { tile.ID.ToString() });
512 foreach(int id in selectedgroup.Tiles) {
514 if (id >= tileset.Tiles.Count){
515 Console.WriteLine("Tile ID is above Tiles.Count: " + id.ToString());
518 tile = (Tile) tileset.Tiles[id];
521 Console.WriteLine("tilegroup contains deleted tile");
524 store.AppendValues(new object[] { id.ToString() });
528 TileList.Model = store;
529 TileList.Selection.Mode = SelectionMode.Multiple;
532 private void FillTileGroupComboBox() {
533 string[] groups = new string[tileset.TileGroups.Count+1];
536 //Submenu submenu = new Submenu();
537 for(int i = 0; i < tileset.TileGroups.Count; ++i) {
538 String tilegroup = ((TileGroup) tileset.TileGroups[i]).Name;
539 groups[i+1] = tilegroup;
540 //submenu.Add(new MenuItem(tilegroup));
542 TileGroupComboBox.PopdownStrings = groups;
543 TileGroupComboBox.Entry.IsEditable = false;
545 //AddTileGroupMenu.Submenu = submenu;
548 protected void OnTileGroupComboBoxEntryActivated(object o, EventArgs args) {
549 if(TileGroupComboBox.Entry.Text == "All") {
550 selectedgroup = null;
552 foreach(TileGroup tilegroup in tileset.TileGroups) {
553 if(tilegroup.Name == TileGroupComboBox.Entry.Text) {
554 selectedgroup = tilegroup;
562 protected void OnTileListCursorChanged(object sender, EventArgs e) {
564 TreePath[] selectpaths =
565 TileList.Selection.GetSelectedRows(out model);
568 foreach(TreePath path in selectpaths) {
570 model.GetIter(out iter, path);
571 int id = Int32.Parse(model.GetValue(iter, 0).ToString());
572 Selection.Add(tileset.Tiles[id]);
577 private void ShowException(Exception e) {
578 MessageDialog dialog = new MessageDialog(MainWindow,
579 DialogFlags.Modal | DialogFlags.DestroyWithParent,
580 MessageType.Error, ButtonsType.Ok,