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