4 using System.Collections;
9 public class Application {
11 private Gtk.Window MainWindow;
13 private Gtk.DrawingArea DrawingArea;
15 private Gtk.CheckButton SolidCheckButton;
17 private Gtk.CheckButton UniSolidCheckButton;
19 private Gtk.CheckButton IceCheckButton;
21 private Gtk.CheckButton WaterCheckButton;
23 private Gtk.CheckButton SlopeCheckButton;
25 private Gtk.CheckButton DontUseCheckButton;
27 private Gtk.CheckButton HiddenCheckButton;
29 private Gtk.Entry DataEntry;
31 private Gtk.Entry AnimFpsEntry;
33 private Gtk.Entry IDEntry;
35 private Gtk.TreeView TileList;
37 private Gtk.Combo TileGroupComboBox;
39 private Gtk.MenuItem AddTileGroupMenu;
42 private Gtk.Dialog RemapDialog;
44 private Gtk.SpinButton RD_spinButton;
46 private string tilesetdir;
47 private string tilesetfile;
48 private TileSet tileset;
49 private TileGroup selectedgroup;
52 private bool[] SelectionArray;
53 private ArrayList Selection = new ArrayList();
56 private bool toggling;
57 private bool selecting;
59 private string currentimage;
60 private Gdk.Pixbuf pixbuf;
62 public static int Main(string[] args) {
63 Gtk.Application.Init();
65 Application app = new Application();
67 /* that's no proper commandlineparsing, but who'll notice... */
69 app.LoadTileSet(args[0]);
71 Gtk.Application.Run();
75 public Application() {
76 Glade.XML gxml = new Glade.XML(null, "tiler.glade", null, null);
77 gxml.Autoconnect(this);
79 if(MainWindow == null || DrawingArea == null || RemapDialog == null)
80 throw new Exception("some widgets not found");
82 DrawingArea.AddEvents((int) Gdk.EventMask.ButtonPressMask);
83 DrawingArea.AddEvents((int) Gdk.EventMask.ButtonReleaseMask);
84 DrawingArea.AddEvents((int) Gdk.EventMask.ButtonMotionMask);
89 protected void OnOpen(object o, EventArgs e) {
90 FileChooserDialog fileChooser = new FileChooserDialog("Select TileSet", MainWindow, FileChooserAction.Open, new object[] {});
92 fileChooser.AddButton(Gtk.Stock.Cancel, Gtk.ResponseType.Cancel);
93 fileChooser.AddButton(Gtk.Stock.Ok, Gtk.ResponseType.Ok);
94 fileChooser.DefaultResponse = Gtk.ResponseType.Ok;
95 Gtk.FileFilter filter;
96 filter = new Gtk.FileFilter();
97 filter.Name = "Supertux tilesets";
98 filter.AddPattern("*.strf");
99 filter.AddPattern("*.stgt");
100 fileChooser.AddFilter( filter );
101 filter = new Gtk.FileFilter();
102 filter.Name = "Supertux 0.1.x tilesets";
103 filter.AddPattern("*.stgt");
104 fileChooser.AddFilter( filter );
105 filter = new Gtk.FileFilter();
106 filter.Name = "Supertux 0.3.x tilesets";
107 filter.AddPattern("*.strf");
108 fileChooser.AddFilter( filter );
109 Gtk.FileFilter all = new Gtk.FileFilter();
110 all.Name = "All Files";
112 fileChooser.AddFilter( all );
113 int result = fileChooser.Run();
116 if(result != (int) ResponseType.Ok)
119 LoadTileSet(fileChooser.Filename);
122 private void LoadTileSet(string file) {
124 tileset = new TileSet();
127 tilesetdir = new FileInfo(file).Directory.ToString();
128 } catch(Exception exception) {
129 ShowException(exception);
134 FillTileGroupComboBox();
138 protected void OnImportImage(object o, EventArgs e) {
139 FileChooserDialog fileChooser = new FileChooserDialog("Select ImageFile", MainWindow, FileChooserAction.Open, new object[] {});
141 fileChooser.AddButton(Gtk.Stock.Cancel, Gtk.ResponseType.Cancel);
142 fileChooser.AddButton(Gtk.Stock.Ok, Gtk.ResponseType.Ok);
143 fileChooser.DefaultResponse = Gtk.ResponseType.Ok;
144 Gtk.FileFilter all = new Gtk.FileFilter();
145 all.Name = "All Files";
147 fileChooser.AddFilter( all );
148 int result = fileChooser.Run();
151 if(result != (int) ResponseType.Ok)
154 string file = fileChooser.Filename;
155 string trim = tilesetdir + "/";
157 if (!file.StartsWith(trim)){
159 "Imported file must be located inside tileset directory");
163 ChangeImage(file.TrimStart(trim.ToCharArray()));
165 int startid = tileset.Tiles.Count;
166 for(int y = 0; y < TilesY; ++y) {
167 for(int x = 0; x < TilesX; ++x) {
169 Tile tile = new Tile();
170 tile.ID = startid + i;
171 ImageRegion region = new ImageRegion();
172 region.ImageFile = currentimage;
173 region.Region = new System.Drawing.Rectangle(x*TileSet.TILE_WIDTH, y*TileSet.TILE_HEIGHT, TileSet.TILE_WIDTH, TileSet.TILE_HEIGHT);
174 tile.Images.Add(region);
175 if(Tiles[i] != null) {
177 "Warning Tile in this region already existed...");
180 tileset.Tiles.Add(tile);
187 private void ChangeImage(string file) {
194 pixbuf = new Pixbuf(tilesetdir + "/" + file);
195 if(pixbuf.Width % TileSet.TILE_WIDTH != 0 || pixbuf.Height % TileSet.TILE_HEIGHT != 0)
196 Console.WriteLine("Warning: Image Width or Height is not a multiple of 32");
197 } catch(Exception e) {
201 currentimage = new FileInfo(file).Name;
202 TilesX = pixbuf.Width / TileSet.TILE_WIDTH;
203 TilesY = pixbuf.Height / TileSet.TILE_HEIGHT;
204 SelectionArray = new bool[TilesX * TilesY];
205 Tiles = new Tile[TilesX * TilesY];
207 // search tileset for tiles with matching image
208 foreach(Tile tile in tileset.Tiles) {
211 if(tile.Images.Count == 0)
213 ImageRegion region = (ImageRegion) tile.Images[0];
214 if(region.ImageFile == currentimage) {
215 int px = region.Region.X / TileSet.TILE_WIDTH;
216 int py = region.Region.Y / TileSet.TILE_HEIGHT;
217 int i = py*TilesX+px;
218 if(i < 0 || i >= Tiles.Length) {
219 Console.WriteLine("Invalid Imageregion at tile " +
223 if(Tiles[i] != null) {
224 Console.WriteLine("Multiple tiles for region " +
225 px*TileSet.TILE_WIDTH + " , " + py*TileSet.TILE_HEIGHT);
232 /* DrawingArea.Allocation
233 = new Gdk.Rectangle(0, 0, pixbuf.Width, pixbuf.Height);*/
234 DrawingArea.WidthRequest = pixbuf.Width;
235 DrawingArea.HeightRequest = pixbuf.Height;
236 DrawingArea.QueueResize();
239 protected void OnSave(object o, EventArgs e) {
242 "Sorry, the file you are editing is too new, there will be huge data loss if you save this.");
244 tileset.Write(tilesetfile);
247 protected void OnQuit(object o, EventArgs e) {
248 Gtk.Application.Quit();
251 protected void OnDeleteQuit(object o, DeleteEventArgs e) {
252 Gtk.Application.Quit();
255 protected void OnAbout(object o, EventArgs e) {
256 // string[] authors = new string[]{
260 Gtk.AboutDialog dialog = new Gtk.AboutDialog();
261 // dialog.Icon = <icon>;
262 dialog.ProgramName = "SuperTux Tiler";
263 dialog.Version = "0.0.3";
264 dialog.Comments = "A tileset editor for SuperTux 0.1.x";
265 // dialog.Authors = authors;
266 dialog.Copyright = "Copyright (c) 2006 SuperTux Devel Team";
268 "This program is free software; you can redistribute it and/or modify" + Environment.NewLine +
269 "it under the terms of the GNU General Public License as published by" + Environment.NewLine +
270 "the Free Software Foundation; either version 2 of the License, or" + Environment.NewLine +
271 "(at your option) any later version." + Environment.NewLine +
272 Environment.NewLine +
273 "This program is distributed in the hope that it will be useful," + Environment.NewLine +
274 "but WITHOUT ANY WARRANTY; without even the implied warranty of" + Environment.NewLine +
275 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the" + Environment.NewLine +
276 "GNU General Public License for more details." + Environment.NewLine +
277 Environment.NewLine +
278 "You should have received a copy of the GNU General Public License" + Environment.NewLine +
279 "along with this program; if not, write to the Free Software Foundation, Inc.," + Environment.NewLine +
280 "59 Temple Place, Suite 330, Boston, MA 02111-1307 USA" + Environment.NewLine;
281 dialog.Website = "http://supertux.lethargik.org/";
282 dialog.WebsiteLabel = "SuperTux on the Web";
287 protected void OnRemapTiles(object o, EventArgs e) {
293 protected void OnRemapDialogCancel(object o, EventArgs e) {
297 protected void OnRemapDialogApply(object o, EventArgs e) {
304 id = RD_spinButton.ValueAsInt;
305 } catch(Exception exception) {
306 ShowException(exception);
311 RD_spinButton.Value = 1;
315 protected void OnCreateTileGroup(object o, EventArgs e) {
318 protected void OnRenameTileGroup(object o, EventArgs e) {
321 protected void RemapTiles(int startID) {
327 foreach(Tile tile in Selection) {
333 // remap in all tilegroups...
334 foreach(TileGroup tilegroup in tileset.TileGroups) {
335 int idx = tilegroup.Tiles.IndexOf(oldid);
337 tilegroup.Tiles[idx] = tile.ID;
345 protected void OnDrawingAreaExpose(object o, ExposeEventArgs e) {
349 Drawable drawable = e.Event.Window;
350 Gdk.GC gc = new Gdk.GC(drawable);
351 drawable.DrawPixbuf(gc, pixbuf, 0, 0, 0, 0,
352 pixbuf.Width, pixbuf.Height, RgbDither.None, 0, 0);
354 gc.RgbFgColor = new Color(0xff, 0, 0);
355 foreach(Tile tile in Selection) {
356 System.Drawing.Rectangle rect
357 = ((ImageRegion) tile.Images[0]).Region;
358 drawable.DrawRectangle(gc, false, rect.X, rect.Y, rect.Width,
365 protected void OnDrawingAreaButtonPress(object o, ButtonPressEventArgs e) {
366 if(SelectionArray == null)
371 for(int i = 0; i < SelectionArray.Length; ++i)
372 SelectionArray[i] = false;
373 select((int) e.Event.X, (int) e.Event.Y);
376 private void select(int x, int y) {
377 int tile = y/TileSet.TILE_HEIGHT * TilesX + x/TileSet.TILE_WIDTH;
378 if(tile < 0 || tile >= SelectionArray.Length)
381 SelectionArray[tile] = true;
382 SelectionArrayChanged();
385 protected void OnDrawingAreaMotionNotify(object i, MotionNotifyEventArgs e) {
388 select((int) e.Event.X, (int) e.Event.Y);
391 protected void OnDrawingAreaButtonRelease(object o, ButtonPressEventArgs e) {
395 protected void OnCheckButtonToggled(object sender, EventArgs e) {
398 foreach(Tile tile in Selection) {
399 if(sender == SolidCheckButton)
400 tile.SetAttribute(Attribute.SOLID, SolidCheckButton.Active);
401 if(sender == UniSolidCheckButton)
402 tile.SetAttribute(Attribute.UNISOLID, UniSolidCheckButton.Active);
403 if(sender == IceCheckButton)
404 tile.SetAttribute(Attribute.ICE, IceCheckButton.Active);
405 if(sender == WaterCheckButton)
406 tile.SetAttribute(Attribute.WATER, WaterCheckButton.Active);
407 if(sender == SlopeCheckButton)
408 tile.SetAttribute(Attribute.SLOPE, SlopeCheckButton.Active);
409 if(sender == HiddenCheckButton)
410 tile.Hidden = HiddenCheckButton.Active;
411 if(sender == DontUseCheckButton)
412 tile.ID = DontUseCheckButton.Active ? -1 : 0;
416 protected void OnEntryChanged(object sender, EventArgs e) {
419 foreach(Tile tile in Selection) {
421 if(sender == IDEntry)
422 tile.ID = Int32.Parse(IDEntry.Text);
423 if(sender == DataEntry)
424 tile.Data = Int32.Parse(DataEntry.Text);
425 if(sender == AnimFpsEntry)
426 tile.AnimFps = Single.Parse(AnimFpsEntry.Text);
428 // ignore parse errors for now...
433 private void SelectionArrayChanged() {
435 for(int i = 0; i < SelectionArray.Length; ++i) {
436 if(!SelectionArray[i])
439 if(Tiles[i] == null) {
440 Console.WriteLine("Tile doesn't exist yet");
441 // TODO ask user to create new tile...
444 Selection.Add(Tiles[i]);
450 private void SelectionChanged() {
453 string nextimage = "";
454 foreach(Tile tile in Selection) {
456 SolidCheckButton.Active = tile.HasAttribute(Attribute.SOLID);
457 UniSolidCheckButton.Active = tile.HasAttribute(Attribute.UNISOLID);
458 IceCheckButton.Active = tile.HasAttribute(Attribute.ICE);
459 WaterCheckButton.Active = tile.HasAttribute(Attribute.WATER);
460 SlopeCheckButton.Active = tile.HasAttribute(Attribute.SLOPE);
461 HiddenCheckButton.Active = tile.Hidden;
462 DontUseCheckButton.Active = tile.ID == -1;
463 DataEntry.Text = tile.Data.ToString();
464 AnimFpsEntry.Text = tile.AnimFps.ToString();
465 IDEntry.Text = tile.ID.ToString();
466 IDEntry.IsEditable = true;
469 if(tile.Images.Count > 0) {
470 nextimage = ((ImageRegion) tile.Images[0]).ImageFile;
473 IDEntry.Text += "," + tile.ID.ToString();
474 IDEntry.IsEditable = false;
475 if(tile.Images.Count > 0
476 && ((ImageRegion) tile.Images[0]).ImageFile != nextimage) {
482 if(nextimage != currentimage)
483 ChangeImage(nextimage);
485 DrawingArea.QueueDraw();
488 private void FillTileList() {
489 TileList.HeadersVisible = true;
490 if(TileList.Columns.Length == 0)
491 TileList.AppendColumn("Tile", new CellRendererText(), "text", 0);
493 ListStore store = new ListStore(typeof(string));
495 if(selectedgroup == null) {
496 foreach(Tile tile in tileset.Tiles) {
499 store.AppendValues(new object[] { tile.ID.ToString() });
502 foreach(int id in selectedgroup.Tiles) {
504 if (id >= tileset.Tiles.Count){
505 Console.WriteLine("Tile ID is above Tiles.Count: " + id.ToString());
508 tile = (Tile) tileset.Tiles[id];
511 Console.WriteLine("tilegroup contains deleted tile");
514 store.AppendValues(new object[] { id.ToString() });
518 TileList.Model = store;
519 TileList.Selection.Mode = SelectionMode.Multiple;
522 private void FillTileGroupComboBox() {
523 string[] groups = new string[tileset.TileGroups.Count+1];
526 //Submenu submenu = new Submenu();
527 for(int i = 0; i < tileset.TileGroups.Count; ++i) {
528 String tilegroup = ((TileGroup) tileset.TileGroups[i]).Name;
529 groups[i+1] = tilegroup;
530 //submenu.Add(new MenuItem(tilegroup));
532 TileGroupComboBox.PopdownStrings = groups;
533 TileGroupComboBox.Entry.IsEditable = false;
535 //AddTileGroupMenu.Submenu = submenu;
538 protected void OnTileGroupComboBoxEntryActivated(object o, EventArgs args) {
539 if(TileGroupComboBox.Entry.Text == "All") {
540 selectedgroup = null;
542 foreach(TileGroup tilegroup in tileset.TileGroups) {
543 if(tilegroup.Name == TileGroupComboBox.Entry.Text) {
544 selectedgroup = tilegroup;
552 protected void OnTileListCursorChanged(object sender, EventArgs e) {
554 TreePath[] selectpaths =
555 TileList.Selection.GetSelectedRows(out model);
558 foreach(TreePath path in selectpaths) {
560 model.GetIter(out iter, path);
561 int id = Int32.Parse(model.GetValue(iter, 0).ToString());
562 Selection.Add(tileset.Tiles[id]);
567 private void ShowException(Exception e) {
568 MessageDialog dialog = new MessageDialog(MainWindow,
569 DialogFlags.Modal | DialogFlags.DestroyWithParent,
570 MessageType.Error, ButtonsType.Ok,