#303: Typo fixes from mathnerd314
[supertux.git] / tools / tilemanager / Application.cs
1 using System;
2 using System.IO;
3 using System.Collections;
4 using Gtk;
5 using Gdk;
6 using Gnome;
7 using Glade;
8
9 public class Application {
10     [Glade.Widget]
11     private Gtk.Window MainWindow;
12     [Glade.Widget]
13     private Gtk.DrawingArea DrawingArea;
14     [Glade.Widget]
15     private Gtk.CheckButton SolidCheckButton;
16     [Glade.Widget]
17     private Gtk.CheckButton UniSolidCheckButton;
18     [Glade.Widget]
19     private Gtk.CheckButton IceCheckButton;
20     [Glade.Widget]
21     private Gtk.CheckButton WaterCheckButton;
22     [Glade.Widget]
23     private Gtk.CheckButton SlopeCheckButton;
24     [Glade.Widget]
25     private Gtk.CheckButton DontUseCheckButton;
26     [Glade.Widget]
27     private Gtk.CheckButton HiddenCheckButton;
28     [Glade.Widget]
29     private Gtk.Entry DataEntry;
30     [Glade.Widget]
31     private Gtk.Entry AnimFpsEntry;
32     [Glade.Widget]
33     private Gtk.Entry IDEntry;
34     [Glade.Widget]
35     private Gnome.AppBar AppBar;
36     [Glade.Widget]
37     private Gtk.VBox MainLayout;
38     [Glade.Widget]
39     private Gtk.TreeView TileList;
40     [Glade.Widget]
41     private Gtk.Combo TileGroupComboBox;
42     [Glade.Widget]
43     private Gtk.MenuItem AddTileGroupMenu;
44
45     private string tilesetdir;
46     private string tilesetfile;
47     private TileSet tileset;
48     private TileGroup selectedgroup;
49
50     private Tile[] Tiles;
51     private bool[] SelectionArray;
52     private ArrayList Selection = new ArrayList();
53     private int TilesX;
54     private int TilesY;
55     private bool toggling;
56     private bool selecting;
57
58     private string currentimage;
59     private Gdk.Pixbuf pixbuf;
60
61     public static int Main(string[] args) {
62         Program kit = new Program("tiler", "0.0.1", Modules.UI, args);
63
64         Application app = new Application();
65
66         /* that's no proper commandlineparsing, but who'll notice... */
67         if(args.Length == 1)
68             app.LoadTileSet(args[0]);
69
70         kit.Run();
71         return 0;
72     }
73
74     public Application() {
75         Glade.XML gxml = new Glade.XML(null, "tiler.glade", null, null);
76         gxml.Autoconnect(this);
77
78         if(MainWindow == null || DrawingArea == null || AppBar == null)
79             throw new Exception("some widgets not found");
80
81         DrawingArea.AddEvents((int) Gdk.EventMask.ButtonPressMask);
82         DrawingArea.AddEvents((int) Gdk.EventMask.ButtonReleaseMask);
83         DrawingArea.AddEvents((int) Gdk.EventMask.ButtonMotionMask);
84
85         // libglade missed interactivity property :-/
86         MainLayout.Remove(AppBar);
87         AppBar = new AppBar(true, true, PreferencesType.Always);
88         AppBar.UserResponse += new EventHandler(OnAppBarUserResponse);
89         MainLayout.PackStart(AppBar, false, false, 0);
90         AppBar.Show();
91
92         TileGroupComboBox.Entry.Activated
93             += new EventHandler (OnTileGroupComboBoxEntryActivated);
94
95         MainWindow.Show();
96     }
97
98     private void OnOpen(object o, EventArgs e) {
99         FileSelection selection = new FileSelection("Select TileSet");
100         selection.OkButton.Clicked += new EventHandler(OnSelectTileSetOk);
101         selection.CancelButton.Clicked += new EventHandler(OnSelectImageCancel);
102         selection.Show();
103     }
104
105     private void OnSelectTileSetOk(object o, EventArgs e) {
106         FileSelection selection = ((FileSelection.FSButton) o).FileSelection;
107         string file = selection.Filename;
108         selection.Destroy();
109
110         LoadTileSet(file);
111     }
112
113     private void LoadTileSet(string file) {
114         try {
115             tileset = new TileSet();
116             tileset.Parse(file);
117             tilesetfile = file;
118             tilesetdir = new FileInfo(file).Directory.ToString();
119         } catch(Exception exception) {
120             ShowException(exception);
121         }
122
123         Selection.Clear();
124         SelectionChanged();
125         FillTileGroupComboBox();
126         FillTileList();
127     }
128
129     private void OnImportImage(object o, EventArgs e) {
130         FileSelection selection = new FileSelection("Select ImageFile");
131         selection.OkButton.Clicked += new EventHandler(OnSelectImageOk);
132         selection.CancelButton.Clicked += new EventHandler(OnSelectImageCancel);
133         selection.Show();
134     }
135
136     private void OnSelectImageCancel(object o, EventArgs args) {
137         FileSelection selection = ((FileSelection.FSButton) o).FileSelection;
138         selection.Destroy();
139     }
140
141     private void OnSelectImageOk(object o, EventArgs args) {
142         FileSelection selection = ((FileSelection.FSButton) o).FileSelection;
143         string file = selection.Filename;
144         selection.Destroy();
145
146         ChangeImage(new FileInfo(file).Name);
147
148         int startid = tileset.Tiles.Count;
149         for(int y = 0; y < TilesY; ++y) {
150             for(int x = 0; x < TilesX; ++x) {
151                 int i = y*TilesX+x;
152                 Tile tile = new Tile();
153                 tile.ID = startid + i;
154                 ImageRegion region = new ImageRegion();
155                 region.ImageFile = currentimage;
156                 region.Region = new System.Drawing.Rectangle(x*32, y*32, 32, 32);
157                 tile.Images.Add(region);
158                 if(Tiles[i] != null) {
159                     Console.WriteLine(
160                             "Warning Tile in this region already existed...");
161                 }
162                 Tiles[i] = tile;
163                 tileset.Tiles.Add(tile);
164             }
165         }
166
167         FillTileList();
168     }
169
170     private void ChangeImage(string file) {
171         if(file == "") {
172             currentimage = "";
173             pixbuf = null;
174             return;
175         }
176         try {
177             pixbuf = new Pixbuf(tilesetdir + "/" + file);
178             if(pixbuf.Width % 32 != 0 || pixbuf.Height % 32 != 0)
179                 Console.WriteLine("Warning: Image Width or Height is not a multiple of 32");
180         } catch(Exception e) {
181             ShowException(e);
182             return;
183         }
184         currentimage = new FileInfo(file).Name;
185         TilesX = pixbuf.Width / 32;
186         TilesY = pixbuf.Height / 32;
187         SelectionArray = new bool[TilesX * TilesY];
188         Tiles = new Tile[TilesX * TilesY];
189
190         // search tileset for tiles with matching image
191         foreach(Tile tile in tileset.Tiles) {
192             if(tile == null)
193                 continue;
194             if(tile.Images.Count == 0)
195                 continue;
196             ImageRegion region = (ImageRegion) tile.Images[0];
197             if(region.ImageFile == currentimage) {
198                 int px = region.Region.X / 32;
199                 int py = region.Region.Y / 32;
200                 int i = py*TilesX+px;
201                 if(i < 0 || i >= Tiles.Length) {
202                     Console.WriteLine("Invalid Imageregion at tile " +
203                             tile.ID);
204                     continue;
205                 }
206                 if(Tiles[i] != null) {
207                     Console.WriteLine("Multiple tiles for region " +
208                             px*32 + " , " + py*32);
209                     continue;
210                 }
211                 Tiles[i] = tile;
212             }
213         }
214
215         /*   DrawingArea.Allocation
216             = new Gdk.Rectangle(0, 0, pixbuf.Width, pixbuf.Height);*/
217         DrawingArea.WidthRequest = pixbuf.Width;
218         DrawingArea.HeightRequest = pixbuf.Height;
219         DrawingArea.QueueResize();
220     }
221
222     private void OnSave(object o, EventArgs e) {
223         tileset.Write(tilesetfile);
224     }
225
226     private void OnQuit(object o, EventArgs e) {
227         Gtk.Application.Quit();
228     }
229
230     private void OnAbout(object o, EventArgs e) {
231     }
232
233     private void OnRemapTiles(object o, EventArgs e) {
234         AppBar.SetPrompt("Start-ID:", true);
235     }
236
237     private void OnCreateTileGroup(object o, EventArgs e) {
238     }
239
240     private void OnRenameTileGroup(object o, EventArgs e) {
241     }
242
243     private void OnAppBarUserResponse(object o, EventArgs e) {
244         try {
245             if(AppBar.Response == null || AppBar.Response == ""
246                     || Tiles == null)
247                 return;
248
249             // remap tiles
250             int id;
251             try {
252                 id = Int32.Parse(AppBar.Response);
253             } catch(Exception exception) {
254                 ShowException(exception);
255                 return;
256             }
257             foreach(Tile tile in Selection) {
258                 if(tile.ID == -1)
259                     continue;
260
261                 int oldid = tile.ID;
262                 tile.ID = id++;
263                 // remap in all tilegroups...
264                 foreach(TileGroup tilegroup in tileset.TileGroups) {
265                     int idx = tilegroup.Tiles.IndexOf(oldid);
266                     if(idx >= 0) {
267                         tilegroup.Tiles[idx] = tile.ID;
268                     }
269                 }
270             }
271             FillTileList();
272             SelectionChanged();
273         } finally {
274             AppBar.ClearPrompt();
275         }
276     }
277
278     private void OnDrawingAreaExpose(object o, ExposeEventArgs e) {
279         if(pixbuf == null)
280             return;
281
282         Drawable drawable = e.Event.Window;
283         Gdk.GC gc = new Gdk.GC(drawable);
284         drawable.DrawPixbuf(gc, pixbuf, 0, 0, 0, 0,
285                 pixbuf.Width, pixbuf.Height, RgbDither.None, 0, 0);
286
287         gc.RgbFgColor = new Color(0xff, 0, 0);
288         foreach(Tile tile in Selection) {
289             System.Drawing.Rectangle rect
290                 = ((ImageRegion) tile.Images[0]).Region;
291             drawable.DrawRectangle(gc, false, rect.X, rect.Y, rect.Width,
292                     rect.Height);
293         }
294
295         e.RetVal = false;
296     }
297
298     private void OnDrawingAreaButtonPress(object o, ButtonPressEventArgs e) {
299         if(SelectionArray == null)
300             return;
301
302         selecting = true;
303
304         for(int i = 0; i < SelectionArray.Length; ++i)
305             SelectionArray[i] = false;
306         select((int) e.Event.X, (int) e.Event.Y);
307     }
308
309     private void select(int x, int y) {
310         int tile = y/32 * TilesX + x/32;
311         if(tile < 0 || tile >= SelectionArray.Length)
312             return;
313
314         SelectionArray[tile] = true;
315         SelectionArrayChanged();
316     }
317
318     private void OnDrawingAreaMotionNotify(object i, MotionNotifyEventArgs e) {
319         if(!selecting)
320             return;
321         select((int) e.Event.X, (int) e.Event.Y);
322     }
323
324     private void OnDrawingAreaButtonRelease(object o, ButtonPressEventArgs e) {
325         selecting = false;
326     }
327
328     private void OnCheckButtonToggled(object sender, EventArgs e) {
329         if(toggling)
330             return;
331         foreach(Tile tile in Selection) {
332             if(sender == SolidCheckButton)
333                 tile.Solid = SolidCheckButton.Active;
334             if(sender == UniSolidCheckButton)
335                 tile.UniSolid = UniSolidCheckButton.Active;
336             if(sender == IceCheckButton)
337                 tile.Ice = IceCheckButton.Active;
338             if(sender == WaterCheckButton)
339                 tile.Water = WaterCheckButton.Active;
340             if(sender == SlopeCheckButton)
341                 tile.Slope = SlopeCheckButton.Active;
342             if(sender == HiddenCheckButton)
343                 tile.Hidden = HiddenCheckButton.Active;
344             if(sender == DontUseCheckButton)
345                 tile.ID = DontUseCheckButton.Active ? -1 : 0;
346         }
347     }
348
349     private void OnEntryChanged(object sender, EventArgs e) {
350         if(toggling)
351             return;
352         foreach(Tile tile in Selection) {
353             try {
354                 if(sender == IDEntry)
355                     tile.ID = Int32.Parse(IDEntry.Text);
356                 if(sender == DataEntry)
357                     tile.Data = Int32.Parse(DataEntry.Text);
358                 if(sender == AnimFpsEntry)
359                     tile.AnimFps = Single.Parse(AnimFpsEntry.Text);
360             } catch(Exception exception) {
361                 // ignore parse errors for now...
362             }
363         }
364     }
365
366     private void SelectionArrayChanged() {
367         Selection.Clear();
368         for(int i = 0; i < SelectionArray.Length; ++i) {
369             if(!SelectionArray[i])
370                 continue;
371
372             if(Tiles[i] == null) {
373                 Console.WriteLine("Tile doesn't exist yet");
374                 // TODO ask user to create new tile...
375                 continue;
376             }
377             Selection.Add(Tiles[i]);
378         }
379
380         SelectionChanged();
381     }
382
383     private void SelectionChanged() {
384         bool first = true;
385         toggling = true;
386         string nextimage = "";
387         foreach(Tile tile in Selection) {
388             if(first) {
389                 SolidCheckButton.Active = tile.Solid;
390                 UniSolidCheckButton.Active = tile.UniSolid;
391                 IceCheckButton.Active = tile.Ice;
392                 WaterCheckButton.Active = tile.Water;
393                 SlopeCheckButton.Active = tile.Slope;
394                 HiddenCheckButton.Active = tile.Hidden;
395                 DontUseCheckButton.Active = tile.ID == -1;
396                 DataEntry.Text = tile.Data.ToString();
397                 AnimFpsEntry.Text = tile.AnimFps.ToString();
398                 IDEntry.Text = tile.ID.ToString();
399                 IDEntry.Editable = true;
400                 first = false;
401
402                 if(tile.Images.Count > 0) {
403                     nextimage = ((ImageRegion) tile.Images[0]).ImageFile;
404                 }
405             } else {
406                 IDEntry.Text += "," + tile.ID.ToString();
407                 IDEntry.Editable = false;
408                 if(tile.Images.Count > 0
409                         && ((ImageRegion) tile.Images[0]).ImageFile != nextimage) {
410                     nextimage = "";
411                     pixbuf = null;
412                 }
413             }
414         }
415         if(nextimage != currentimage)
416             ChangeImage(nextimage);
417         toggling = false;
418         DrawingArea.QueueDraw();
419     }
420
421     private void FillTileList() {
422         TileList.HeadersVisible = true;
423         if(TileList.Columns.Length == 0)
424             TileList.AppendColumn("Tile", new CellRendererText(), "text", 0);
425
426         ListStore store = new ListStore(typeof(string));
427
428         if(selectedgroup == null) {
429             foreach(Tile tile in tileset.Tiles) {
430                 if(tile == null)
431                     continue;
432                 store.AppendValues(new object[] { tile.ID.ToString() });
433             }
434         } else {
435             foreach(int id in selectedgroup.Tiles) {
436                 Tile tile = (Tile) tileset.Tiles[id];
437                 if(tile == null) {
438                     Console.WriteLine("tilegroup contains deleted tile");
439                     continue;
440                 }
441                 store.AppendValues(new object[] { id.ToString() });
442             }
443         }
444
445         TileList.Model = store;
446         TileList.Selection.Mode = SelectionMode.Multiple;
447     }
448
449     private void FillTileGroupComboBox() {
450         string[] groups = new string[tileset.TileGroups.Count+1];
451         groups[0] = "All";
452
453         //Submenu submenu = new Submenu();
454         for(int i = 0; i < tileset.TileGroups.Count; ++i) {
455             String tilegroup = ((TileGroup) tileset.TileGroups[i]).Name;
456             groups[i+1] = tilegroup;
457             //submenu.Add(new MenuItem(tilegroup));
458         }
459         TileGroupComboBox.PopdownStrings = groups;
460         TileGroupComboBox.Entry.Editable = false;
461
462         //AddTileGroupMenu.Submenu = submenu;
463     }
464
465     private void OnTileGroupComboBoxEntryActivated(object o, EventArgs args) {
466         if(TileGroupComboBox.Entry.Text == "All") {
467             selectedgroup = null;
468         } else {
469             foreach(TileGroup tilegroup in tileset.TileGroups) {
470                 if(tilegroup.Name == TileGroupComboBox.Entry.Text) {
471                     selectedgroup = tilegroup;
472                     break;
473                 }
474             }
475         }
476         FillTileList();
477     }
478
479     private void OnTileListCursorChanged(object sender, EventArgs e) {
480         TreeModel model;
481         TreePath[] selectpaths =
482             TileList.Selection.GetSelectedRows(out model);
483
484         Selection.Clear();
485         foreach(TreePath path in selectpaths) {
486             TreeIter iter;
487             model.GetIter(out iter, path);
488             int id = Int32.Parse(model.GetValue(iter, 0).ToString());
489             Selection.Add(tileset.Tiles[id]);
490         }
491         SelectionChanged();
492     }
493
494     private void ShowException(Exception e) {
495         MessageDialog dialog = new MessageDialog(MainWindow,
496                 DialogFlags.Modal | DialogFlags.DestroyWithParent,
497                 MessageType.Error, ButtonsType.Ok,
498                 e.Message);
499         dialog.Run();
500         dialog.Destroy();
501     }
502 }