4 using System.Collections;
9 public class Application {
11 private Gtk.Window MainWindow;
13 private Gtk.CheckMenuItem useNewSyntax;
15 private Gtk.DrawingArea DrawingArea;
18 private Gtk.CheckButton SolidCheckButton;
20 private Gtk.CheckButton UniSolidCheckButton;
22 private Gtk.CheckButton IceCheckButton;
24 private Gtk.CheckButton WaterCheckButton;
26 private Gtk.CheckButton HurtsCheckButton;
28 private Gtk.CheckButton FireCheckButton;
30 private Gtk.CheckButton BrickCheckButton;
32 private Gtk.CheckButton CoinCheckButton;
34 private Gtk.CheckButton FullBoxCheckButton;
36 private Gtk.CheckButton SlopeCheckButton;
38 private Gtk.CheckButton GoalCheckButton;
40 private Gtk.CheckButton DontUseCheckButton;
42 private Gtk.CheckButton HiddenCheckButton;
45 private Gtk.Entry DataEntry;
47 private Gtk.Entry AnimFpsEntry;
49 private Gtk.Entry IDEntry;
51 private Gtk.TreeView TileList;
53 private Gtk.Combo TileGroupComboBox;
55 private Gtk.MenuItem AddTileGroupMenu;
58 private Gtk.Dialog RemapDialog;
60 private Gtk.SpinButton RD_spinButton;
62 private string tilesetdir;
63 private string tilesetfile;
64 private TileSet tileset;
65 private TileGroup selectedgroup;
68 private bool[] SelectionArray;
69 private ArrayList Selection = new ArrayList();
72 private bool toggling;
73 private bool selecting;
75 private string currentimage;
76 private Gdk.Pixbuf pixbuf;
78 public static int Main(string[] args) {
79 Gtk.Application.Init();
81 Application app = new Application();
83 /* that's no proper commandlineparsing, but who'll notice... */
85 app.LoadTileSet(args[0]);
87 Gtk.Application.Run();
91 public Application() {
92 Glade.XML gxml = new Glade.XML(null, "tiler.glade", null, null);
93 gxml.Autoconnect(this);
95 if(MainWindow == null || DrawingArea == null || RemapDialog == null)
96 throw new Exception("some widgets not found");
98 DrawingArea.AddEvents((int) Gdk.EventMask.ButtonPressMask);
99 DrawingArea.AddEvents((int) Gdk.EventMask.ButtonReleaseMask);
100 DrawingArea.AddEvents((int) Gdk.EventMask.ButtonMotionMask);
105 protected void OnOpen(object o, EventArgs e) {
106 FileChooserDialog fileChooser = new FileChooserDialog("Select TileSet", MainWindow, FileChooserAction.Open, new object[] {});
108 fileChooser.AddButton(Gtk.Stock.Cancel, Gtk.ResponseType.Cancel);
109 fileChooser.AddButton(Gtk.Stock.Ok, Gtk.ResponseType.Ok);
110 fileChooser.DefaultResponse = Gtk.ResponseType.Ok;
111 Gtk.FileFilter filter;
112 filter = new Gtk.FileFilter();
113 filter.Name = "Supertux tilesets";
114 filter.AddPattern("*.strf");
115 filter.AddPattern("*.stgt");
116 fileChooser.AddFilter( filter );
117 filter = new Gtk.FileFilter();
118 filter.Name = "Supertux 0.1.x tilesets";
119 filter.AddPattern("*.stgt");
120 fileChooser.AddFilter( filter );
121 filter = new Gtk.FileFilter();
122 filter.Name = "Supertux 0.3.x tilesets";
123 filter.AddPattern("*.strf");
124 fileChooser.AddFilter( filter );
125 Gtk.FileFilter all = new Gtk.FileFilter();
126 all.Name = "All Files";
128 fileChooser.AddFilter( all );
129 int result = fileChooser.Run();
132 if(result != (int) ResponseType.Ok)
135 LoadTileSet(fileChooser.Filename);
138 private void LoadTileSet(string file) {
140 tileset = new TileSet();
143 tilesetdir = new FileInfo(file).Directory.ToString();
144 } catch(Exception exception) {
145 ShowException(exception);
150 FillTileGroupComboBox();
153 useNewSyntax.Active = tileset.IsNew;
155 Console.WriteLine("Warning: new syntax of 0.3.x files \"More tiles in one image\" isn't currently supported for WRITING");
159 protected void OnImportImage(object o, EventArgs e) {
160 FileChooserDialog fileChooser = new FileChooserDialog("Select ImageFile", MainWindow, FileChooserAction.Open, new object[] {});
162 fileChooser.AddButton(Gtk.Stock.Cancel, Gtk.ResponseType.Cancel);
163 fileChooser.AddButton(Gtk.Stock.Ok, Gtk.ResponseType.Ok);
164 fileChooser.DefaultResponse = Gtk.ResponseType.Ok;
165 Gtk.FileFilter all = new Gtk.FileFilter();
166 all.Name = "All Files";
168 fileChooser.AddFilter( all );
169 int result = fileChooser.Run();
172 if(result != (int) ResponseType.Ok)
175 string file = fileChooser.Filename;
176 string trim = tilesetdir + "/";
178 if (!file.StartsWith(trim)){
180 "Imported file must be located inside tileset directory");
184 ChangeImage(file.TrimStart(trim.ToCharArray()));
186 int startid = tileset.Tiles.Count;
187 for(int y = 0; y < TilesY; ++y) {
188 for(int x = 0; x < TilesX; ++x) {
190 Tile tile = new Tile();
191 tile.ID = startid + i;
192 ImageRegion region = new ImageRegion();
193 region.ImageFile = currentimage;
194 region.Region = new System.Drawing.Rectangle(x*TileSet.TILE_WIDTH, y*TileSet.TILE_HEIGHT, TileSet.TILE_WIDTH, TileSet.TILE_HEIGHT);
195 tile.Images.Add(region);
196 if(Tiles[i] != null) {
198 "Warning Tile in this region already existed...");
201 tileset.Tiles.Add(tile);
208 private void ChangeImage(string file) {
215 pixbuf = new Pixbuf(tilesetdir + "/" + file);
216 if(pixbuf.Width % TileSet.TILE_WIDTH != 0 || pixbuf.Height % TileSet.TILE_HEIGHT != 0)
217 Console.WriteLine("Warning: Image Width or Height is not a multiple of 32");
218 } catch(Exception e) {
222 currentimage = new FileInfo(file).Name;
223 TilesX = pixbuf.Width / TileSet.TILE_WIDTH;
224 TilesY = pixbuf.Height / TileSet.TILE_HEIGHT;
225 SelectionArray = new bool[TilesX * TilesY];
226 Tiles = new Tile[TilesX * TilesY];
228 // search tileset for tiles with matching image
229 foreach(Tile tile in tileset.Tiles) {
232 if(tile.Images.Count == 0)
234 ImageRegion region = (ImageRegion) tile.Images[0];
235 if(region.ImageFile == currentimage) {
236 int px = region.Region.X / TileSet.TILE_WIDTH;
237 int py = region.Region.Y / TileSet.TILE_HEIGHT;
238 int i = py*TilesX+px;
239 if(i < 0 || i >= Tiles.Length) {
240 Console.WriteLine("Invalid Imageregion at tile " +
244 if(Tiles[i] != null) {
245 Console.WriteLine("Multiple tiles for region " +
246 px*TileSet.TILE_WIDTH + " , " + py*TileSet.TILE_HEIGHT);
253 /* DrawingArea.Allocation
254 = new Gdk.Rectangle(0, 0, pixbuf.Width, pixbuf.Height);*/
255 DrawingArea.WidthRequest = pixbuf.Width;
256 DrawingArea.HeightRequest = pixbuf.Height;
257 DrawingArea.QueueResize();
260 protected void OnSave(object o, EventArgs e) {
261 if (tileset.IsNew && useNewSyntax.Active) {
262 MessageDialog dialog = new MessageDialog(MainWindow, DialogFlags.Modal | DialogFlags.DestroyWithParent, MessageType.Error, ButtonsType.Ok,
263 "Sorry, the file you are editing is too new and 0.3.x syntax is not supported yet.");
267 tileset.Write(tilesetfile);
271 protected void OnQuit(object o, EventArgs e) {
272 Gtk.Application.Quit();
275 protected void OnDeleteQuit(object o, DeleteEventArgs e) {
276 Gtk.Application.Quit();
279 protected void OnAbout(object o, EventArgs e) {
280 // string[] authors = new string[]{
284 Gtk.AboutDialog dialog = new Gtk.AboutDialog();
285 // dialog.Icon = <icon>;
286 dialog.ProgramName = "SuperTux Tiler";
287 dialog.Version = "0.0.3";
288 dialog.Comments = "A tileset editor for SuperTux 0.1.x";
289 // dialog.Authors = authors;
290 dialog.Copyright = "Copyright (c) 2006 SuperTux Devel Team";
292 "This program is free software; you can redistribute it and/or modify" + Environment.NewLine +
293 "it under the terms of the GNU General Public License as published by" + Environment.NewLine +
294 "the Free Software Foundation; either version 2 of the License, or" + Environment.NewLine +
295 "(at your option) any later version." + Environment.NewLine +
296 Environment.NewLine +
297 "This program is distributed in the hope that it will be useful," + Environment.NewLine +
298 "but WITHOUT ANY WARRANTY; without even the implied warranty of" + Environment.NewLine +
299 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the" + Environment.NewLine +
300 "GNU General Public License for more details." + Environment.NewLine +
301 Environment.NewLine +
302 "You should have received a copy of the GNU General Public License" + Environment.NewLine +
303 "along with this program; if not, write to the Free Software Foundation, Inc.," + Environment.NewLine +
304 "59 Temple Place, Suite 330, Boston, MA 02111-1307 USA" + Environment.NewLine;
305 dialog.Website = "http://supertux.lethargik.org/";
306 dialog.WebsiteLabel = "SuperTux on the Web";
311 protected void OnRemapTiles(object o, EventArgs e) {
317 protected void OnRemapDialogCancel(object o, EventArgs e) {
321 protected void OnRemapDialogApply(object o, EventArgs e) {
328 id = RD_spinButton.ValueAsInt;
329 } catch(Exception exception) {
330 ShowException(exception);
335 RD_spinButton.Value = 1;
339 protected void OnCreateTileGroup(object o, EventArgs e) {
342 protected void OnRenameTileGroup(object o, EventArgs e) {
345 protected void RemapTiles(int startID) {
351 foreach(Tile tile in Selection) {
357 // remap in all tilegroups...
358 foreach(TileGroup tilegroup in tileset.TileGroups) {
359 int idx = tilegroup.Tiles.IndexOf(oldid);
361 tilegroup.Tiles[idx] = tile.ID;
369 protected void OnDrawingAreaExpose(object o, ExposeEventArgs e) {
373 Drawable drawable = e.Event.Window;
374 Gdk.GC gc = new Gdk.GC(drawable);
375 drawable.DrawPixbuf(gc, pixbuf, 0, 0, 0, 0,
376 pixbuf.Width, pixbuf.Height, RgbDither.None, 0, 0);
378 gc.RgbFgColor = new Color(0xff, 0, 0);
379 foreach(Tile tile in Selection) {
380 System.Drawing.Rectangle rect
381 = ((ImageRegion) tile.Images[0]).Region;
382 drawable.DrawRectangle(gc, false, rect.X, rect.Y, rect.Width,
389 protected void OnDrawingAreaButtonPress(object o, ButtonPressEventArgs e) {
390 if(SelectionArray == null)
395 for(int i = 0; i < SelectionArray.Length; ++i)
396 SelectionArray[i] = false;
397 select((int) e.Event.X, (int) e.Event.Y);
400 private void select(int x, int y) {
401 int tile = y/TileSet.TILE_HEIGHT * TilesX + x/TileSet.TILE_WIDTH;
402 if(tile < 0 || tile >= SelectionArray.Length)
405 SelectionArray[tile] = true;
406 SelectionArrayChanged();
409 protected void OnDrawingAreaMotionNotify(object i, MotionNotifyEventArgs e) {
412 select((int) e.Event.X, (int) e.Event.Y);
415 protected void OnDrawingAreaButtonRelease(object o, ButtonPressEventArgs e) {
419 protected void OnCheckButtonToggled(object sender, EventArgs e) {
422 foreach(Tile tile in Selection) {
423 if(sender == SolidCheckButton)
424 tile.SetAttribute(Attribute.SOLID, SolidCheckButton.Active);
425 if(sender == UniSolidCheckButton)
426 tile.SetAttribute(Attribute.UNISOLID, UniSolidCheckButton.Active);
427 if(sender == IceCheckButton)
428 tile.SetAttribute(Attribute.ICE, IceCheckButton.Active);
429 if(sender == WaterCheckButton)
430 tile.SetAttribute(Attribute.WATER, WaterCheckButton.Active);
431 if(sender == HurtsCheckButton)
432 tile.SetAttribute(Attribute.HURTS, HurtsCheckButton.Active);
433 if(sender == FireCheckButton)
434 tile.SetAttribute(Attribute.FIRE, FireCheckButton.Active);
435 if(sender == BrickCheckButton)
436 tile.SetAttribute(Attribute.BRICK, BrickCheckButton.Active);
437 if(sender == CoinCheckButton)
438 tile.SetAttribute(Attribute.COIN, CoinCheckButton.Active);
439 if(sender == FullBoxCheckButton)
440 tile.SetAttribute(Attribute.FULLBOX, FullBoxCheckButton.Active);
441 if(sender == SlopeCheckButton)
442 tile.SetAttribute(Attribute.SLOPE, SlopeCheckButton.Active);
443 if(sender == GoalCheckButton)
444 tile.SetAttribute(Attribute.GOAL, GoalCheckButton.Active);
445 if(sender == HiddenCheckButton)
446 tile.Hidden = HiddenCheckButton.Active;
447 if(sender == DontUseCheckButton)
448 tile.ID = DontUseCheckButton.Active ? -1 : 0;
452 protected void OnEntryChanged(object sender, EventArgs e) {
455 foreach(Tile tile in Selection) {
457 if(sender == IDEntry)
458 tile.ID = Int32.Parse(IDEntry.Text);
459 if(sender == DataEntry)
460 tile.Data = Int32.Parse(DataEntry.Text);
461 if(sender == AnimFpsEntry)
462 tile.AnimFps = Single.Parse(AnimFpsEntry.Text);
464 // ignore parse errors for now...
469 private void SelectionArrayChanged() {
471 for(int i = 0; i < SelectionArray.Length; ++i) {
472 if(!SelectionArray[i])
475 if(Tiles[i] == null) {
476 Console.WriteLine("Tile doesn't exist yet");
477 // TODO ask user to create new tile...
480 Selection.Add(Tiles[i]);
486 private void SelectionChanged() {
489 string nextimage = "";
490 foreach(Tile tile in Selection) {
492 SolidCheckButton.Active = tile.HasAttribute(Attribute.SOLID);
493 UniSolidCheckButton.Active = tile.HasAttribute(Attribute.UNISOLID);
494 IceCheckButton.Active = tile.HasAttribute(Attribute.ICE);
495 WaterCheckButton.Active = tile.HasAttribute(Attribute.WATER);
496 HurtsCheckButton.Active = tile.HasAttribute(Attribute.HURTS);
497 FireCheckButton.Active = tile.HasAttribute(Attribute.FIRE);
498 BrickCheckButton.Active = tile.HasAttribute(Attribute.BRICK);
499 CoinCheckButton.Active = tile.HasAttribute(Attribute.COIN);
500 FullBoxCheckButton.Active = tile.HasAttribute(Attribute.FULLBOX);
501 SlopeCheckButton.Active = tile.HasAttribute(Attribute.SLOPE);
502 GoalCheckButton.Active = tile.HasAttribute(Attribute.GOAL);
503 HiddenCheckButton.Active = tile.Hidden;
504 DontUseCheckButton.Active = tile.ID == -1;
505 DataEntry.Text = tile.Data.ToString();
506 AnimFpsEntry.Text = tile.AnimFps.ToString();
507 IDEntry.Text = tile.ID.ToString();
508 IDEntry.IsEditable = true;
511 if(tile.Images.Count > 0) {
512 nextimage = ((ImageRegion) tile.Images[0]).ImageFile;
515 IDEntry.Text += "," + tile.ID.ToString();
516 IDEntry.IsEditable = false;
517 if(tile.Images.Count > 0
518 && ((ImageRegion) tile.Images[0]).ImageFile != nextimage) {
524 if(nextimage != currentimage)
525 ChangeImage(nextimage);
527 DrawingArea.QueueDraw();
530 private void FillTileList() {
531 TileList.HeadersVisible = true;
532 if(TileList.Columns.Length == 0)
533 TileList.AppendColumn("Tile", new CellRendererText(), "text", 0);
535 ListStore store = new ListStore(typeof(string));
537 if(selectedgroup == null) {
538 foreach(Tile tile in tileset.Tiles) {
541 store.AppendValues(new object[] { tile.ID.ToString() });
544 foreach(int id in selectedgroup.Tiles) {
546 if (id >= tileset.Tiles.Count){
547 Console.WriteLine("Tile ID is above Tiles.Count: " + id.ToString());
550 tile = (Tile) tileset.Tiles[id];
553 Console.WriteLine("tilegroup contains deleted tile");
556 store.AppendValues(new object[] { id.ToString() });
560 TileList.Model = store;
561 TileList.Selection.Mode = SelectionMode.Multiple;
564 private void FillTileGroupComboBox() {
565 string[] groups = new string[tileset.TileGroups.Count+1];
568 //Submenu submenu = new Submenu();
569 for(int i = 0; i < tileset.TileGroups.Count; ++i) {
570 String tilegroup = ((TileGroup) tileset.TileGroups[i]).Name;
571 groups[i+1] = tilegroup;
572 //submenu.Add(new MenuItem(tilegroup));
574 TileGroupComboBox.PopdownStrings = groups;
575 TileGroupComboBox.Entry.IsEditable = false;
577 //AddTileGroupMenu.Submenu = submenu;
580 protected void OnTileGroupComboBoxEntryActivated(object o, EventArgs args) {
581 if(TileGroupComboBox.Entry.Text == "All") {
582 selectedgroup = null;
584 foreach(TileGroup tilegroup in tileset.TileGroups) {
585 if(tilegroup.Name == TileGroupComboBox.Entry.Text) {
586 selectedgroup = tilegroup;
594 protected void OnTileListCursorChanged(object sender, EventArgs e) {
596 TreePath[] selectpaths =
597 TileList.Selection.GetSelectedRows(out model);
600 foreach(TreePath path in selectpaths) {
602 model.GetIter(out iter, path);
603 int id = Int32.Parse(model.GetValue(iter, 0).ToString());
604 Selection.Add(tileset.Tiles[id]);
609 private void ShowException(Exception e) {
610 MessageDialog dialog = new MessageDialog(MainWindow,
611 DialogFlags.Modal | DialogFlags.DestroyWithParent,
612 MessageType.Error, ButtonsType.Ok,