3 using System.Collections;
8 public class Application {
10 private Gtk.Window MainWindow;
12 private Gtk.DrawingArea DrawingArea;
14 private Gtk.CheckButton SolidCheckButton;
16 private Gtk.CheckButton UniSolidCheckButton;
18 private Gtk.CheckButton IceCheckButton;
20 private Gtk.CheckButton WaterCheckButton;
22 private Gtk.CheckButton SlopeCheckButton;
24 private Gtk.CheckButton DontUseCheckButton;
26 private Gtk.CheckButton HiddenCheckButton;
28 private Gtk.Entry DataEntry;
30 private Gtk.Entry AnimFpsEntry;
32 private Gtk.Entry IDEntry;
34 private Gtk.TreeView TileList;
36 private Gtk.Combo TileGroupComboBox;
38 private Gtk.MenuItem AddTileGroupMenu;
41 private Gtk.Dialog RemapDialog;
43 private Gtk.SpinButton RD_spinButton;
45 private string tilesetdir;
46 private string tilesetfile;
47 private TileSet tileset;
48 private TileGroup selectedgroup;
51 private bool[] SelectionArray;
52 private ArrayList Selection = new ArrayList();
55 private bool toggling;
56 private bool selecting;
58 private string currentimage;
59 private Gdk.Pixbuf pixbuf;
61 public static int Main(string[] args) {
62 Gtk.Application.Init();
64 Application app = new Application();
66 /* that's no proper commandlineparsing, but who'll notice... */
68 app.LoadTileSet(args[0]);
70 Gtk.Application.Run();
74 public Application() {
75 Glade.XML gxml = new Glade.XML(null, "tiler.glade", null, null);
76 gxml.Autoconnect(this);
78 if(MainWindow == null || DrawingArea == null || RemapDialog == null)
79 throw new Exception("some widgets not found");
81 DrawingArea.AddEvents((int) Gdk.EventMask.ButtonPressMask);
82 DrawingArea.AddEvents((int) Gdk.EventMask.ButtonReleaseMask);
83 DrawingArea.AddEvents((int) Gdk.EventMask.ButtonMotionMask);
88 protected void OnOpen(object o, EventArgs e) {
89 FileChooserDialog fileChooser = new FileChooserDialog("Select TileSet", MainWindow, FileChooserAction.Open, new object[] {});
91 fileChooser.AddButton(Gtk.Stock.Cancel, Gtk.ResponseType.Cancel);
92 fileChooser.AddButton(Gtk.Stock.Ok, Gtk.ResponseType.Ok);
93 fileChooser.DefaultResponse = Gtk.ResponseType.Ok;
94 Gtk.FileFilter filter;
95 filter = new Gtk.FileFilter();
96 filter.Name = "Supertux 0.1.x tilesets";
97 filter.AddPattern("*.stgt");
98 fileChooser.AddFilter( filter );
99 filter = new Gtk.FileFilter();
100 filter.Name = "Supertux tilesets";
101 filter.AddPattern("*.strf");
102 filter.AddPattern("*.stgt");
103 fileChooser.AddFilter( filter );
104 filter = new Gtk.FileFilter();
105 filter.Name = "Supertux 0.3.x tilesets";
106 filter.AddPattern("*.strf");
107 fileChooser.AddFilter( filter );
108 Gtk.FileFilter all = new Gtk.FileFilter();
109 all.Name = "All Files";
111 fileChooser.AddFilter( all );
112 int result = fileChooser.Run();
115 if(result != (int) ResponseType.Ok)
118 LoadTileSet(fileChooser.Filename);
121 private void LoadTileSet(string file) {
123 tileset = new TileSet();
126 tilesetdir = new FileInfo(file).Directory.ToString();
127 } catch(Exception exception) {
128 ShowException(exception);
133 FillTileGroupComboBox();
137 protected void OnImportImage(object o, EventArgs e) {
138 FileChooserDialog fileChooser = new FileChooserDialog("Select ImageFile", MainWindow, FileChooserAction.Open, new object[] {});
140 fileChooser.AddButton(Gtk.Stock.Cancel, Gtk.ResponseType.Cancel);
141 fileChooser.AddButton(Gtk.Stock.Ok, Gtk.ResponseType.Ok);
142 fileChooser.DefaultResponse = Gtk.ResponseType.Ok;
143 Gtk.FileFilter all = new Gtk.FileFilter();
144 all.Name = "All Files";
146 fileChooser.AddFilter( all );
147 int result = fileChooser.Run();
150 if(result != (int) ResponseType.Ok)
153 string file = fileChooser.Filename;
154 string trim = tilesetdir + "/";
156 if (!file.StartsWith(trim)){
158 "Imported file must be located inside tileset directory");
162 ChangeImage(file.TrimStart(trim.ToCharArray()));
164 int startid = tileset.Tiles.Count;
165 for(int y = 0; y < TilesY; ++y) {
166 for(int x = 0; x < TilesX; ++x) {
168 Tile tile = new Tile();
169 tile.ID = startid + i;
170 ImageRegion region = new ImageRegion();
171 region.ImageFile = currentimage;
172 region.Region = new System.Drawing.Rectangle(x*32, y*32, 32, 32);
173 tile.Images.Add(region);
174 if(Tiles[i] != null) {
176 "Warning Tile in this region already existed...");
179 tileset.Tiles.Add(tile);
186 private void ChangeImage(string file) {
193 pixbuf = new Pixbuf(tilesetdir + "/" + file);
194 if(pixbuf.Width % 32 != 0 || pixbuf.Height % 32 != 0)
195 Console.WriteLine("Warning: Image Width or Height is not a multiple of 32");
196 } catch(Exception e) {
200 currentimage = new FileInfo(file).Name;
201 TilesX = pixbuf.Width / 32;
202 TilesY = pixbuf.Height / 32;
203 SelectionArray = new bool[TilesX * TilesY];
204 Tiles = new Tile[TilesX * TilesY];
206 // search tileset for tiles with matching image
207 foreach(Tile tile in tileset.Tiles) {
210 if(tile.Images.Count == 0)
212 ImageRegion region = (ImageRegion) tile.Images[0];
213 if(region.ImageFile == currentimage) {
214 int px = region.Region.X / 32;
215 int py = region.Region.Y / 32;
216 int i = py*TilesX+px;
217 if(i < 0 || i >= Tiles.Length) {
218 Console.WriteLine("Invalid Imageregion at tile " +
222 if(Tiles[i] != null) {
223 Console.WriteLine("Multiple tiles for region " +
224 px*32 + " , " + py*32);
231 /* DrawingArea.Allocation
232 = new Gdk.Rectangle(0, 0, pixbuf.Width, pixbuf.Height);*/
233 DrawingArea.WidthRequest = pixbuf.Width;
234 DrawingArea.HeightRequest = pixbuf.Height;
235 DrawingArea.QueueResize();
238 protected void OnSave(object o, EventArgs e) {
241 "Sorry, the file you are editing is too new, there will be huge data loss if you save this.");
243 tileset.Write(tilesetfile);
246 protected void OnQuit(object o, EventArgs e) {
247 Gtk.Application.Quit();
250 protected void OnDeleteQuit(object o, DeleteEventArgs e) {
251 Gtk.Application.Quit();
254 protected void OnAbout(object o, EventArgs e) {
255 // string[] authors = new string[]{
259 Gtk.AboutDialog dialog = new Gtk.AboutDialog();
260 // dialog.Icon = <icon>;
261 dialog.ProgramName = "SuperTux Tiler";
262 dialog.Version = "0.0.3";
263 dialog.Comments = "A tileset editor for SuperTux 0.1.x";
264 // dialog.Authors = authors;
265 dialog.Copyright = "Copyright (c) 2006 SuperTux Devel Team";
267 "This program is free software; you can redistribute it and/or modify" + Environment.NewLine +
268 "it under the terms of the GNU General Public License as published by" + Environment.NewLine +
269 "the Free Software Foundation; either version 2 of the License, or" + Environment.NewLine +
270 "(at your option) any later version." + Environment.NewLine +
271 Environment.NewLine +
272 "This program is distributed in the hope that it will be useful," + Environment.NewLine +
273 "but WITHOUT ANY WARRANTY; without even the implied warranty of" + Environment.NewLine +
274 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the" + Environment.NewLine +
275 "GNU General Public License for more details." + Environment.NewLine +
276 Environment.NewLine +
277 "You should have received a copy of the GNU General Public License" + Environment.NewLine +
278 "along with this program; if not, write to the Free Software Foundation, Inc.," + Environment.NewLine +
279 "59 Temple Place, Suite 330, Boston, MA 02111-1307 USA" + Environment.NewLine;
280 dialog.Website = "http://supertux.lethargik.org/";
281 dialog.WebsiteLabel = "SuperTux on the Web";
286 protected void OnRemapTiles(object o, EventArgs e) {
292 protected void OnRemapDialogCancel(object o, EventArgs e) {
296 protected void OnRemapDialogApply(object o, EventArgs e) {
303 id = RD_spinButton.ValueAsInt;
304 } catch(Exception exception) {
305 ShowException(exception);
310 RD_spinButton.Value = 1;
314 protected void OnCreateTileGroup(object o, EventArgs e) {
317 protected void OnRenameTileGroup(object o, EventArgs e) {
320 protected void RemapTiles(int startID) {
326 foreach(Tile tile in Selection) {
332 // remap in all tilegroups...
333 foreach(TileGroup tilegroup in tileset.TileGroups) {
334 int idx = tilegroup.Tiles.IndexOf(oldid);
336 tilegroup.Tiles[idx] = tile.ID;
344 protected void OnDrawingAreaExpose(object o, ExposeEventArgs e) {
348 Drawable drawable = e.Event.Window;
349 Gdk.GC gc = new Gdk.GC(drawable);
350 drawable.DrawPixbuf(gc, pixbuf, 0, 0, 0, 0,
351 pixbuf.Width, pixbuf.Height, RgbDither.None, 0, 0);
353 gc.RgbFgColor = new Color(0xff, 0, 0);
354 foreach(Tile tile in Selection) {
355 System.Drawing.Rectangle rect
356 = ((ImageRegion) tile.Images[0]).Region;
357 drawable.DrawRectangle(gc, false, rect.X, rect.Y, rect.Width,
364 protected void OnDrawingAreaButtonPress(object o, ButtonPressEventArgs e) {
365 if(SelectionArray == null)
370 for(int i = 0; i < SelectionArray.Length; ++i)
371 SelectionArray[i] = false;
372 select((int) e.Event.X, (int) e.Event.Y);
375 private void select(int x, int y) {
376 int tile = y/32 * TilesX + x/32;
377 if(tile < 0 || tile >= SelectionArray.Length)
380 SelectionArray[tile] = true;
381 SelectionArrayChanged();
384 protected void OnDrawingAreaMotionNotify(object i, MotionNotifyEventArgs e) {
387 select((int) e.Event.X, (int) e.Event.Y);
390 protected void OnDrawingAreaButtonRelease(object o, ButtonPressEventArgs e) {
394 protected void OnCheckButtonToggled(object sender, EventArgs e) {
397 foreach(Tile tile in Selection) {
398 if(sender == SolidCheckButton)
399 tile.SetAttribute(Attribute.SOLID, SolidCheckButton.Active);
400 if(sender == UniSolidCheckButton)
401 tile.SetAttribute(Attribute.UNISOLID, UniSolidCheckButton.Active);
402 if(sender == IceCheckButton)
403 tile.SetAttribute(Attribute.ICE, IceCheckButton.Active);
404 if(sender == WaterCheckButton)
405 tile.SetAttribute(Attribute.WATER, WaterCheckButton.Active);
406 if(sender == SlopeCheckButton)
407 tile.SetAttribute(Attribute.SLOPE, SlopeCheckButton.Active);
408 if(sender == HiddenCheckButton)
409 tile.Hidden = HiddenCheckButton.Active;
410 if(sender == DontUseCheckButton)
411 tile.ID = DontUseCheckButton.Active ? -1 : 0;
415 protected void OnEntryChanged(object sender, EventArgs e) {
418 foreach(Tile tile in Selection) {
420 if(sender == IDEntry)
421 tile.ID = Int32.Parse(IDEntry.Text);
422 if(sender == DataEntry)
423 tile.Data = Int32.Parse(DataEntry.Text);
424 if(sender == AnimFpsEntry)
425 tile.AnimFps = Single.Parse(AnimFpsEntry.Text);
427 // ignore parse errors for now...
432 private void SelectionArrayChanged() {
434 for(int i = 0; i < SelectionArray.Length; ++i) {
435 if(!SelectionArray[i])
438 if(Tiles[i] == null) {
439 Console.WriteLine("Tile doesn't exist yet");
440 // TODO ask user to create new tile...
443 Selection.Add(Tiles[i]);
449 private void SelectionChanged() {
452 string nextimage = "";
453 foreach(Tile tile in Selection) {
455 SolidCheckButton.Active = tile.HasAttribute(Attribute.SOLID);
456 UniSolidCheckButton.Active = tile.HasAttribute(Attribute.UNISOLID);
457 IceCheckButton.Active = tile.HasAttribute(Attribute.ICE);
458 WaterCheckButton.Active = tile.HasAttribute(Attribute.WATER);
459 SlopeCheckButton.Active = tile.HasAttribute(Attribute.SLOPE);
460 HiddenCheckButton.Active = tile.Hidden;
461 DontUseCheckButton.Active = tile.ID == -1;
462 DataEntry.Text = tile.Data.ToString();
463 AnimFpsEntry.Text = tile.AnimFps.ToString();
464 IDEntry.Text = tile.ID.ToString();
465 IDEntry.IsEditable = true;
468 if(tile.Images.Count > 0) {
469 nextimage = ((ImageRegion) tile.Images[0]).ImageFile;
472 IDEntry.Text += "," + tile.ID.ToString();
473 IDEntry.IsEditable = false;
474 if(tile.Images.Count > 0
475 && ((ImageRegion) tile.Images[0]).ImageFile != nextimage) {
481 if(nextimage != currentimage)
482 ChangeImage(nextimage);
484 DrawingArea.QueueDraw();
487 private void FillTileList() {
488 TileList.HeadersVisible = true;
489 if(TileList.Columns.Length == 0)
490 TileList.AppendColumn("Tile", new CellRendererText(), "text", 0);
492 ListStore store = new ListStore(typeof(string));
494 if(selectedgroup == null) {
495 foreach(Tile tile in tileset.Tiles) {
498 store.AppendValues(new object[] { tile.ID.ToString() });
501 foreach(int id in selectedgroup.Tiles) {
503 if (id >= tileset.Tiles.Count){
504 Console.WriteLine("Tile ID is above Tiles.Count: " + id.ToString());
507 tile = (Tile) tileset.Tiles[id];
510 Console.WriteLine("tilegroup contains deleted tile");
513 store.AppendValues(new object[] { id.ToString() });
517 TileList.Model = store;
518 TileList.Selection.Mode = SelectionMode.Multiple;
521 private void FillTileGroupComboBox() {
522 string[] groups = new string[tileset.TileGroups.Count+1];
525 //Submenu submenu = new Submenu();
526 for(int i = 0; i < tileset.TileGroups.Count; ++i) {
527 String tilegroup = ((TileGroup) tileset.TileGroups[i]).Name;
528 groups[i+1] = tilegroup;
529 //submenu.Add(new MenuItem(tilegroup));
531 TileGroupComboBox.PopdownStrings = groups;
532 TileGroupComboBox.Entry.IsEditable = false;
534 //AddTileGroupMenu.Submenu = submenu;
537 protected void OnTileGroupComboBoxEntryActivated(object o, EventArgs args) {
538 if(TileGroupComboBox.Entry.Text == "All") {
539 selectedgroup = null;
541 foreach(TileGroup tilegroup in tileset.TileGroups) {
542 if(tilegroup.Name == TileGroupComboBox.Entry.Text) {
543 selectedgroup = tilegroup;
551 protected void OnTileListCursorChanged(object sender, EventArgs e) {
553 TreePath[] selectpaths =
554 TileList.Selection.GetSelectedRows(out model);
557 foreach(TreePath path in selectpaths) {
559 model.GetIter(out iter, path);
560 int id = Int32.Parse(model.GetValue(iter, 0).ToString());
561 Selection.Add(tileset.Tiles[id]);
566 private void ShowException(Exception e) {
567 MessageDialog dialog = new MessageDialog(MainWindow,
568 DialogFlags.Modal | DialogFlags.DestroyWithParent,
569 MessageType.Error, ButtonsType.Ok,