Application.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. // $Id$
  2. using System;
  3. using System.IO;
  4. using System.Collections;
  5. using Gtk;
  6. using Gdk;
  7. using Glade;
  8. public class Application {
  9. [Glade.Widget]
  10. private Gtk.Window MainWindow;
  11. [Glade.Widget]
  12. private Gtk.CheckMenuItem useNewSyntax;
  13. [Glade.Widget]
  14. private Gtk.DrawingArea DrawingArea;
  15. [Glade.Widget]
  16. //Flags CheckButtons
  17. private Gtk.CheckButton SolidCheckButton;
  18. [Glade.Widget]
  19. private Gtk.CheckButton UniSolidCheckButton;
  20. [Glade.Widget]
  21. private Gtk.CheckButton IceCheckButton;
  22. [Glade.Widget]
  23. private Gtk.CheckButton WaterCheckButton;
  24. [Glade.Widget]
  25. private Gtk.CheckButton HurtsCheckButton;
  26. [Glade.Widget]
  27. private Gtk.CheckButton FireCheckButton;
  28. [Glade.Widget]
  29. private Gtk.CheckButton BrickCheckButton;
  30. [Glade.Widget]
  31. private Gtk.CheckButton CoinCheckButton;
  32. [Glade.Widget]
  33. private Gtk.CheckButton FullBoxCheckButton;
  34. [Glade.Widget]
  35. private Gtk.CheckButton SlopeCheckButton;
  36. [Glade.Widget]
  37. private Gtk.CheckButton GoalCheckButton;
  38. [Glade.Widget]
  39. private Gtk.CheckButton DontUseCheckButton;
  40. [Glade.Widget]
  41. private Gtk.CheckButton HiddenCheckButton;
  42. [Glade.Widget]
  43. private Gtk.Entry DataEntry;
  44. [Glade.Widget]
  45. private Gtk.Entry AnimFpsEntry;
  46. [Glade.Widget]
  47. private Gtk.Entry IDEntry;
  48. [Glade.Widget]
  49. private Gtk.TreeView TileList;
  50. [Glade.Widget]
  51. private Gtk.Combo TileGroupComboBox;
  52. [Glade.Widget]
  53. private Gtk.MenuItem AddTileGroupMenu;
  54. [Glade.Widget]
  55. private Gtk.Dialog RemapDialog;
  56. [Glade.Widget]
  57. private Gtk.SpinButton RD_spinButton;
  58. private string tilesetdir;
  59. private string tilesetfile;
  60. private TileSet tileset;
  61. private TileGroup selectedgroup;
  62. private Tile[] Tiles;
  63. private bool[] SelectionArray;
  64. private ArrayList Selection = new ArrayList();
  65. private int TilesX;
  66. private int TilesY;
  67. private bool toggling;
  68. private bool selecting;
  69. private string currentimage;
  70. private Gdk.Pixbuf pixbuf;
  71. public static int Main(string[] args) {
  72. Gtk.Application.Init();
  73. Application app = new Application();
  74. /* that's no proper commandlineparsing, but who'll notice... */
  75. if(args.Length == 1)
  76. app.LoadTileSet(args[0]);
  77. Gtk.Application.Run();
  78. return 0;
  79. }
  80. public Application() {
  81. Glade.XML gxml = new Glade.XML(null, "tiler.glade", null, null);
  82. gxml.Autoconnect(this);
  83. if(MainWindow == null || DrawingArea == null || RemapDialog == null)
  84. throw new Exception("some widgets not found");
  85. DrawingArea.AddEvents((int) Gdk.EventMask.ButtonPressMask);
  86. DrawingArea.AddEvents((int) Gdk.EventMask.ButtonReleaseMask);
  87. DrawingArea.AddEvents((int) Gdk.EventMask.ButtonMotionMask);
  88. MainWindow.Show();
  89. }
  90. protected void OnOpen(object o, EventArgs e) {
  91. 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";
  111. all.AddPattern("*");
  112. fileChooser.AddFilter( all );
  113. int result = fileChooser.Run();
  114. fileChooser.Hide();
  115. if(result != (int) ResponseType.Ok)
  116. return;
  117. LoadTileSet(fileChooser.Filename);
  118. }
  119. private void LoadTileSet(string file) {
  120. try {
  121. tileset = new TileSet();
  122. tileset.Parse(file);
  123. tilesetfile = file;
  124. tilesetdir = new FileInfo(file).Directory.ToString();
  125. } catch(Exception exception) {
  126. ShowException(exception);
  127. }
  128. Selection.Clear();
  129. SelectionChanged();
  130. FillTileGroupComboBox();
  131. FillTileList();
  132. useNewSyntax.Active = tileset.IsNew;
  133. if (tileset.IsNew)
  134. Console.WriteLine("Warning: new syntax of 0.3.x files \"More tiles in one image\" isn't currently supported for WRITING");
  135. }
  136. protected void OnImportImage(object o, EventArgs e) {
  137. FileChooserDialog fileChooser = new FileChooserDialog("Select ImageFile", MainWindow, FileChooserAction.Open, new object[] {});
  138. fileChooser.AddButton(Gtk.Stock.Cancel, Gtk.ResponseType.Cancel);
  139. fileChooser.AddButton(Gtk.Stock.Ok, Gtk.ResponseType.Ok);
  140. fileChooser.DefaultResponse = Gtk.ResponseType.Ok;
  141. Gtk.FileFilter all = new Gtk.FileFilter();
  142. all.Name = "All Files";
  143. all.AddPattern("*");
  144. fileChooser.AddFilter( all );
  145. int result = fileChooser.Run();
  146. fileChooser.Hide();
  147. if(result != (int) ResponseType.Ok)
  148. return;
  149. string file = fileChooser.Filename;
  150. string trim = tilesetdir + "/";
  151. if (!file.StartsWith(trim)){
  152. Console.WriteLine(
  153. "Imported file must be located inside tileset directory");
  154. return;
  155. }
  156. ChangeImage(file.TrimStart(trim.ToCharArray()));
  157. int startid = tileset.Tiles.Count;
  158. for(int y = 0; y < TilesY; ++y) {
  159. for(int x = 0; x < TilesX; ++x) {
  160. int i = y*TilesX+x;
  161. Tile tile = new Tile();
  162. tile.ID = startid + i;
  163. ImageRegion region = new ImageRegion();
  164. region.ImageFile = currentimage;
  165. region.Region = new System.Drawing.Rectangle(x*TileSet.TILE_WIDTH, y*TileSet.TILE_HEIGHT, TileSet.TILE_WIDTH, TileSet.TILE_HEIGHT);
  166. tile.Images.Add(region);
  167. if(Tiles[i] != null) {
  168. Console.WriteLine(
  169. "Warning Tile in this region already existed...");
  170. }
  171. Tiles[i] = tile;
  172. tileset.Tiles.Add(tile);
  173. }
  174. }
  175. FillTileList();
  176. }
  177. private void ChangeImage(string file) {
  178. if(file == "") {
  179. currentimage = "";
  180. pixbuf = null;
  181. return;
  182. }
  183. try {
  184. pixbuf = new Pixbuf(tilesetdir + "/" + file);
  185. if(pixbuf.Width % TileSet.TILE_WIDTH != 0 || pixbuf.Height % TileSet.TILE_HEIGHT != 0)
  186. Console.WriteLine("Warning: Image Width or Height is not a multiple of 32");
  187. } catch(Exception e) {
  188. ShowException(e);
  189. return;
  190. }
  191. currentimage = new FileInfo(file).Name;
  192. TilesX = pixbuf.Width / TileSet.TILE_WIDTH;
  193. TilesY = pixbuf.Height / TileSet.TILE_HEIGHT;
  194. SelectionArray = new bool[TilesX * TilesY];
  195. Tiles = new Tile[TilesX * TilesY];
  196. // search tileset for tiles with matching image
  197. foreach(Tile tile in tileset.Tiles) {
  198. if(tile == null)
  199. continue;
  200. if(tile.Images.Count == 0)
  201. continue;
  202. ImageRegion region = (ImageRegion) tile.Images[0];
  203. if(region.ImageFile == currentimage) {
  204. int px = region.Region.X / TileSet.TILE_WIDTH;
  205. int py = region.Region.Y / TileSet.TILE_HEIGHT;
  206. int i = py*TilesX+px;
  207. if(i < 0 || i >= Tiles.Length) {
  208. Console.WriteLine("Invalid Imageregion at tile " +
  209. tile.ID);
  210. continue;
  211. }
  212. if(Tiles[i] != null) {
  213. Console.WriteLine("Multiple tiles for region " +
  214. px*TileSet.TILE_WIDTH + " , " + py*TileSet.TILE_HEIGHT);
  215. continue;
  216. }
  217. Tiles[i] = tile;
  218. }
  219. }
  220. /* DrawingArea.Allocation
  221. = new Gdk.Rectangle(0, 0, pixbuf.Width, pixbuf.Height);*/
  222. DrawingArea.WidthRequest = pixbuf.Width;
  223. DrawingArea.HeightRequest = pixbuf.Height;
  224. DrawingArea.QueueResize();
  225. }
  226. protected void OnSave(object o, EventArgs e) {
  227. if (tileset.IsNew && useNewSyntax.Active) {
  228. MessageDialog dialog = new MessageDialog(MainWindow, DialogFlags.Modal | DialogFlags.DestroyWithParent, MessageType.Error, ButtonsType.Ok,
  229. "Sorry, the file you are editing is too new and 0.3.x syntax is not supported yet.");
  230. dialog.Run();
  231. dialog.Destroy();
  232. } else {
  233. tileset.Write(tilesetfile);
  234. }
  235. }
  236. protected void OnQuit(object o, EventArgs e) {
  237. Gtk.Application.Quit();
  238. }
  239. protected void OnDeleteQuit(object o, DeleteEventArgs e) {
  240. Gtk.Application.Quit();
  241. }
  242. protected void OnAbout(object o, EventArgs e) {
  243. // string[] authors = new string[]{
  244. // "<autors?>",
  245. // };
  246. Gtk.AboutDialog dialog = new Gtk.AboutDialog();
  247. // dialog.Icon = <icon>;
  248. dialog.ProgramName = "SuperTux Tiler";
  249. dialog.Version = "0.0.3";
  250. dialog.Comments = "A tileset editor for SuperTux 0.1.x";
  251. // dialog.Authors = authors;
  252. dialog.Copyright = "Copyright (c) 2006 SuperTux Devel Team";
  253. dialog.License =
  254. "This program is free software; you can redistribute it and/or modify" + Environment.NewLine +
  255. "it under the terms of the GNU General Public License as published by" + Environment.NewLine +
  256. "the Free Software Foundation; either version 2 of the License, or" + Environment.NewLine +
  257. "(at your option) any later version." + Environment.NewLine +
  258. Environment.NewLine +
  259. "This program is distributed in the hope that it will be useful," + Environment.NewLine +
  260. "but WITHOUT ANY WARRANTY; without even the implied warranty of" + Environment.NewLine +
  261. "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the" + Environment.NewLine +
  262. "GNU General Public License for more details." + Environment.NewLine +
  263. Environment.NewLine +
  264. "You should have received a copy of the GNU General Public License" + Environment.NewLine +
  265. "along with this program; if not, write to the Free Software Foundation, Inc.," + Environment.NewLine +
  266. "59 Temple Place, Suite 330, Boston, MA 02111-1307 USA" + Environment.NewLine;
  267. dialog.Website = "http://supertux.lethargik.org/";
  268. dialog.WebsiteLabel = "SuperTux on the Web";
  269. dialog.Run();
  270. dialog.Destroy();
  271. }
  272. protected void OnRemapTiles(object o, EventArgs e) {
  273. if(Tiles == null)
  274. return;
  275. RemapDialog.Show();
  276. }
  277. protected void OnRemapDialogCancel(object o, EventArgs e) {
  278. RemapDialog.Hide();
  279. }
  280. protected void OnRemapDialogApply(object o, EventArgs e) {
  281. RemapDialog.Hide();
  282. try {
  283. // remap tiles
  284. int id;
  285. try {
  286. id = RD_spinButton.ValueAsInt;
  287. } catch(Exception exception) {
  288. ShowException(exception);
  289. return;
  290. }
  291. RemapTiles(id);
  292. } finally {
  293. RD_spinButton.Value = 1;
  294. }
  295. }
  296. protected void OnCreateTileGroup(object o, EventArgs e) {
  297. }
  298. protected void OnRenameTileGroup(object o, EventArgs e) {
  299. }
  300. protected void RemapTiles(int startID) {
  301. if(Tiles == null)
  302. return;
  303. // remap tiles
  304. int id = startID;
  305. foreach(Tile tile in Selection) {
  306. if(tile.ID == -1)
  307. continue;
  308. int oldid = tile.ID;
  309. tile.ID = id++;
  310. // remap in all tilegroups...
  311. foreach(TileGroup tilegroup in tileset.TileGroups) {
  312. int idx = tilegroup.Tiles.IndexOf(oldid);
  313. if(idx >= 0) {
  314. tilegroup.Tiles[idx] = tile.ID;
  315. }
  316. }
  317. }
  318. FillTileList();
  319. SelectionChanged();
  320. }
  321. protected void OnDrawingAreaExpose(object o, ExposeEventArgs e) {
  322. if(pixbuf == null)
  323. return;
  324. Drawable drawable = e.Event.Window;
  325. Gdk.GC gc = new Gdk.GC(drawable);
  326. drawable.DrawPixbuf(gc, pixbuf, 0, 0, 0, 0,
  327. pixbuf.Width, pixbuf.Height, RgbDither.None, 0, 0);
  328. gc.RgbFgColor = new Color(0xff, 0, 0);
  329. foreach(Tile tile in Selection) {
  330. System.Drawing.Rectangle rect
  331. = ((ImageRegion) tile.Images[0]).Region;
  332. drawable.DrawRectangle(gc, false, rect.X, rect.Y, rect.Width,
  333. rect.Height);
  334. }
  335. e.RetVal = false;
  336. }
  337. protected void OnDrawingAreaButtonPress(object o, ButtonPressEventArgs e) {
  338. if(SelectionArray == null)
  339. return;
  340. selecting = true;
  341. for(int i = 0; i < SelectionArray.Length; ++i)
  342. SelectionArray[i] = false;
  343. select((int) e.Event.X, (int) e.Event.Y);
  344. }
  345. private void select(int x, int y) {
  346. int tile = y/TileSet.TILE_HEIGHT * TilesX + x/TileSet.TILE_WIDTH;
  347. if(tile < 0 || tile >= SelectionArray.Length)
  348. return;
  349. SelectionArray[tile] = true;
  350. SelectionArrayChanged();
  351. }
  352. protected void OnDrawingAreaMotionNotify(object i, MotionNotifyEventArgs e) {
  353. if(!selecting)
  354. return;
  355. select((int) e.Event.X, (int) e.Event.Y);
  356. }
  357. protected void OnDrawingAreaButtonRelease(object o, ButtonPressEventArgs e) {
  358. selecting = false;
  359. }
  360. protected void OnCheckButtonToggled(object sender, EventArgs e) {
  361. if(toggling)
  362. return;
  363. foreach(Tile tile in Selection) {
  364. if(sender == SolidCheckButton)
  365. tile.SetAttribute(Attribute.SOLID, SolidCheckButton.Active);
  366. if(sender == UniSolidCheckButton)
  367. tile.SetAttribute(Attribute.UNISOLID, UniSolidCheckButton.Active);
  368. if(sender == IceCheckButton)
  369. tile.SetAttribute(Attribute.ICE, IceCheckButton.Active);
  370. if(sender == WaterCheckButton)
  371. tile.SetAttribute(Attribute.WATER, WaterCheckButton.Active);
  372. if(sender == HurtsCheckButton)
  373. tile.SetAttribute(Attribute.HURTS, HurtsCheckButton.Active);
  374. if(sender == FireCheckButton)
  375. tile.SetAttribute(Attribute.FIRE, FireCheckButton.Active);
  376. if(sender == BrickCheckButton)
  377. tile.SetAttribute(Attribute.BRICK, BrickCheckButton.Active);
  378. if(sender == CoinCheckButton)
  379. tile.SetAttribute(Attribute.COIN, CoinCheckButton.Active);
  380. if(sender == FullBoxCheckButton)
  381. tile.SetAttribute(Attribute.FULLBOX, FullBoxCheckButton.Active);
  382. if(sender == SlopeCheckButton)
  383. tile.SetAttribute(Attribute.SLOPE, SlopeCheckButton.Active);
  384. if(sender == GoalCheckButton)
  385. tile.SetAttribute(Attribute.GOAL, GoalCheckButton.Active);
  386. if(sender == HiddenCheckButton)
  387. tile.Hidden = HiddenCheckButton.Active;
  388. if(sender == DontUseCheckButton)
  389. tile.ID = DontUseCheckButton.Active ? -1 : 0;
  390. }
  391. }
  392. protected void OnEntryChanged(object sender, EventArgs e) {
  393. if(toggling)
  394. return;
  395. foreach(Tile tile in Selection) {
  396. try {
  397. if(sender == IDEntry)
  398. tile.ID = Int32.Parse(IDEntry.Text);
  399. if(sender == DataEntry)
  400. tile.Data = Int32.Parse(DataEntry.Text);
  401. if(sender == AnimFpsEntry)
  402. tile.AnimFps = Single.Parse(AnimFpsEntry.Text);
  403. } catch(Exception) {
  404. // ignore parse errors for now...
  405. }
  406. }
  407. }
  408. private void SelectionArrayChanged() {
  409. Selection.Clear();
  410. for(int i = 0; i < SelectionArray.Length; ++i) {
  411. if(!SelectionArray[i])
  412. continue;
  413. if(Tiles[i] == null) {
  414. Console.WriteLine("Tile doesn't exist yet");
  415. // TODO ask user to create new tile...
  416. continue;
  417. }
  418. Selection.Add(Tiles[i]);
  419. }
  420. SelectionChanged();
  421. }
  422. private void SelectionChanged() {
  423. bool first = true;
  424. toggling = true;
  425. string nextimage = "";
  426. foreach(Tile tile in Selection) {
  427. if(first) {
  428. SolidCheckButton.Active = tile.HasAttribute(Attribute.SOLID);
  429. UniSolidCheckButton.Active = tile.HasAttribute(Attribute.UNISOLID);
  430. IceCheckButton.Active = tile.HasAttribute(Attribute.ICE);
  431. WaterCheckButton.Active = tile.HasAttribute(Attribute.WATER);
  432. HurtsCheckButton.Active = tile.HasAttribute(Attribute.HURTS);
  433. FireCheckButton.Active = tile.HasAttribute(Attribute.FIRE);
  434. BrickCheckButton.Active = tile.HasAttribute(Attribute.BRICK);
  435. CoinCheckButton.Active = tile.HasAttribute(Attribute.COIN);
  436. FullBoxCheckButton.Active = tile.HasAttribute(Attribute.FULLBOX);
  437. SlopeCheckButton.Active = tile.HasAttribute(Attribute.SLOPE);
  438. GoalCheckButton.Active = tile.HasAttribute(Attribute.GOAL);
  439. HiddenCheckButton.Active = tile.Hidden;
  440. DontUseCheckButton.Active = tile.ID == -1;
  441. DataEntry.Text = tile.Data.ToString();
  442. AnimFpsEntry.Text = tile.AnimFps.ToString();
  443. IDEntry.Text = tile.ID.ToString();
  444. IDEntry.IsEditable = true;
  445. first = false;
  446. if(tile.Images.Count > 0) {
  447. nextimage = ((ImageRegion) tile.Images[0]).ImageFile;
  448. }
  449. } else {
  450. IDEntry.Text += "," + tile.ID.ToString();
  451. IDEntry.IsEditable = false;
  452. if(tile.Images.Count > 0
  453. && ((ImageRegion) tile.Images[0]).ImageFile != nextimage) {
  454. nextimage = "";
  455. pixbuf = null;
  456. }
  457. }
  458. }
  459. if(nextimage != currentimage)
  460. ChangeImage(nextimage);
  461. toggling = false;
  462. DrawingArea.QueueDraw();
  463. }
  464. private void FillTileList() {
  465. TileList.HeadersVisible = true;
  466. if(TileList.Columns.Length == 0)
  467. TileList.AppendColumn("Tile", new CellRendererText(), "text", 0);
  468. ListStore store = new ListStore(typeof(string));
  469. if(selectedgroup == null) {
  470. foreach(Tile tile in tileset.Tiles) {
  471. if(tile == null)
  472. continue;
  473. store.AppendValues(new object[] { tile.ID.ToString() });
  474. }
  475. } else {
  476. foreach(int id in selectedgroup.Tiles) {
  477. Tile tile;
  478. if (id >= tileset.Tiles.Count){
  479. Console.WriteLine("Tile ID is above Tiles.Count: " + id.ToString());
  480. continue;
  481. } else {
  482. tile = (Tile) tileset.Tiles[id];
  483. }
  484. if(tile == null) {
  485. Console.WriteLine("tilegroup contains deleted tile");
  486. continue;
  487. }
  488. store.AppendValues(new object[] { id.ToString() });
  489. }
  490. }
  491. TileList.Model = store;
  492. TileList.Selection.Mode = SelectionMode.Multiple;
  493. }
  494. private void FillTileGroupComboBox() {
  495. string[] groups = new string[tileset.TileGroups.Count+1];
  496. groups[0] = "All";
  497. //Submenu submenu = new Submenu();
  498. for(int i = 0; i < tileset.TileGroups.Count; ++i) {
  499. String tilegroup = ((TileGroup) tileset.TileGroups[i]).Name;
  500. groups[i+1] = tilegroup;
  501. //submenu.Add(new MenuItem(tilegroup));
  502. }
  503. TileGroupComboBox.PopdownStrings = groups;
  504. TileGroupComboBox.Entry.IsEditable = false;
  505. //AddTileGroupMenu.Submenu = submenu;
  506. }
  507. protected void OnTileGroupComboBoxEntryActivated(object o, EventArgs args) {
  508. if(TileGroupComboBox.Entry.Text == "All") {
  509. selectedgroup = null;
  510. } else {
  511. foreach(TileGroup tilegroup in tileset.TileGroups) {
  512. if(tilegroup.Name == TileGroupComboBox.Entry.Text) {
  513. selectedgroup = tilegroup;
  514. break;
  515. }
  516. }
  517. }
  518. FillTileList();
  519. }
  520. protected void OnTileListCursorChanged(object sender, EventArgs e) {
  521. TreeModel model;
  522. TreePath[] selectpaths =
  523. TileList.Selection.GetSelectedRows(out model);
  524. Selection.Clear();
  525. foreach(TreePath path in selectpaths) {
  526. TreeIter iter;
  527. model.GetIter(out iter, path);
  528. int id = Int32.Parse(model.GetValue(iter, 0).ToString());
  529. Selection.Add(tileset.Tiles[id]);
  530. }
  531. SelectionChanged();
  532. }
  533. private void ShowException(Exception e) {
  534. MessageDialog dialog = new MessageDialog(MainWindow,
  535. DialogFlags.Modal | DialogFlags.DestroyWithParent,
  536. MessageType.Error, ButtonsType.Ok,
  537. e.Message);
  538. dialog.Run();
  539. dialog.Destroy();
  540. }
  541. }