ObjectListWidget.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. // SuperTux Editor
  2. // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. using System;
  17. using System.Collections.Generic;
  18. using OpenGl;
  19. using Sprites;
  20. using DataStructures;
  21. using Gtk;
  22. using Gdk;
  23. public class ObjectListWidget : GLWidgetBase
  24. {
  25. private const int TILE_WIDTH = 32;
  26. private const int TILE_HEIGHT = 32;
  27. private const int SPACING_X = 1;
  28. private const int SPACING_Y = 1;
  29. private const int ROW_HEIGHT = TILE_HEIGHT + SPACING_Y;
  30. private const int COLUMN_WIDTH = TILE_WIDTH + SPACING_X;
  31. private const int NONE = -1;
  32. private int TILES_PER_ROW = 4;
  33. private int ROWS_PER_PAGE = 10;
  34. private bool objectsLoaded;
  35. private List<Type> gameObjectTypes = new List<Type>();
  36. private List<Sprite> gameObjectSprites = new List<Sprite>();
  37. private int SelectedObjectNr = NONE;
  38. private int FirstRow = 0;
  39. private Application application;
  40. private Level level;
  41. private Adjustment vadjustment;
  42. private int LastObjectHovered;
  43. public static TargetEntry [] DragTargetEntries = new TargetEntry[] {
  44. new TargetEntry("GameObject", TargetFlags.App, 0),
  45. new TargetEntry("BadguyName", TargetFlags.App, 1)
  46. };
  47. public ObjectListWidget(Application application, Adjustment adjv)
  48. {
  49. this.application = application;
  50. SetSizeRequest( COLUMN_WIDTH * TILES_PER_ROW, -1);
  51. ButtonPressEvent += OnButtonPress;
  52. AddEvents((int) Gdk.EventMask.ButtonPressMask);
  53. AddEvents((int) Gdk.EventMask.AllEventsMask);
  54. AddEvents((int) Gdk.EventMask.ScrollMask);
  55. Gtk.Drag.SourceSet (this, Gdk.ModifierType.Button1Mask,
  56. DragTargetEntries, DragAction.Copy | DragAction.Default);
  57. SizeAllocated += OnSizeAllocated;
  58. DragBegin += OnDragBegin;
  59. DragEnd += OnDragEnd;
  60. ScrollEvent += OnScroll;
  61. DragDataGet += OnDragDataGet;
  62. application.LevelChanged += OnLevelChanged;
  63. vadjustment = adjv;
  64. vadjustment.ValueChanged += OnVAdjustmentChangedValue;
  65. HasTooltip = true;
  66. QueryTooltip += OnQueryTooltip;
  67. }
  68. /// <summary>Redraw Widget</summary>
  69. protected override void DrawGl()
  70. {
  71. LoadObjectImages();
  72. gl.Clear(gl.COLOR_BUFFER_BIT);
  73. int x = 0;
  74. int y = 0;
  75. float scalex = 1;
  76. float scaley = 1;
  77. Sprite objectSprite = null;
  78. for( int i = 0 + FirstRow * TILES_PER_ROW; i < gameObjectTypes.Count; i++ ){
  79. objectSprite = gameObjectSprites[i];
  80. //Draw Image
  81. if( objectSprite != null ){
  82. gl.PushMatrix();
  83. //Adjust Size
  84. scalex = scaley = 1;
  85. if( objectSprite.Width > TILE_WIDTH ) {
  86. scalex = TILE_WIDTH / objectSprite.Width;
  87. }
  88. if( objectSprite.Height > TILE_HEIGHT ){
  89. scaley = TILE_HEIGHT / objectSprite.Height;
  90. }
  91. //keep aspect ratio
  92. if( scalex < scaley ) {
  93. scaley = scalex;
  94. } else {
  95. scalex = scaley;
  96. }
  97. gl.Translatef(x, y, 0);
  98. gl.Scalef( scalex, scaley, 1 );
  99. objectSprite.Draw(objectSprite.Offset);
  100. gl.PopMatrix();
  101. }
  102. //mark the selected object
  103. if( i == SelectedObjectNr ){
  104. gl.Color4f(0, 1, 1, 0.4f);
  105. gl.Disable(gl.TEXTURE_2D);
  106. gl.Begin(gl.QUADS);
  107. gl.Vertex2f( x, y );
  108. gl.Vertex2f( x + TILE_WIDTH, y );
  109. gl.Vertex2f( x + TILE_WIDTH, y + TILE_HEIGHT);
  110. gl.Vertex2f( x, y + TILE_HEIGHT);
  111. gl.End();
  112. gl.Enable(gl.TEXTURE_2D);
  113. gl.Color4f(1, 1, 1, 1);
  114. }
  115. x += COLUMN_WIDTH;
  116. if( x >= TILES_PER_ROW * COLUMN_WIDTH ) {
  117. x = 0;
  118. y += ROW_HEIGHT;
  119. }
  120. }
  121. }
  122. /// <summary>Create object list</summary>
  123. /// <remarks>Loading Images need Gl context so this has to be called from DrawGl</remarks>
  124. private void LoadObjectImages()
  125. {
  126. if(objectsLoaded)
  127. return;
  128. // Reinitialize
  129. gameObjectTypes.Clear();
  130. gameObjectSprites.Clear();
  131. // The null object (arrow)
  132. gameObjectTypes.Add(null);
  133. gameObjectSprites.Add(CreateSprite("images/engine/editor/arrow.png", null));
  134. foreach(Type type in this.GetType().Assembly.GetTypes()) {
  135. SupertuxObjectAttribute objectAttribute
  136. = (SupertuxObjectAttribute) Attribute.GetCustomAttribute(type, typeof(SupertuxObjectAttribute));
  137. if(objectAttribute == null)
  138. continue;
  139. if (objectAttribute.Target == SupertuxObjectAttribute.Usage.None)
  140. continue;
  141. // We load all objects if no level is loaded to avoid crash
  142. // when accessing the level object (as that is null
  143. // when no level is loaded).
  144. if (this.level != null) {
  145. if ( (objectAttribute.Target == SupertuxObjectAttribute.Usage.WorldmapOnly) &&
  146. (!level.isWorldmap) ) {
  147. continue;
  148. } else if ( (objectAttribute.Target == SupertuxObjectAttribute.Usage.LevelOnly) &&
  149. (level.isWorldmap) ) {
  150. continue;
  151. }
  152. }
  153. Sprite icon = CreateSprite(objectAttribute.IconSprite, objectAttribute.ObjectListAction);
  154. if( icon == null ) { //no sprite, no image, no can do.
  155. LogManager.Log(LogLevel.Warning, "ObjectListWidget: Can't create an icon for " + objectAttribute.Name
  156. + " from " +objectAttribute.IconSprite);
  157. } else {
  158. gameObjectTypes.Add(type);
  159. gameObjectSprites.Add(icon);
  160. }
  161. }
  162. objectsLoaded = true;
  163. updateScrollbar();
  164. }
  165. private static Sprite CreateSprite(string name, string action)
  166. {
  167. Sprite result = null;
  168. // Might be a sprite
  169. try{
  170. result = SpriteManager.Create(name);
  171. } catch {
  172. }
  173. if( result != null ){ // Try to find a nice action.
  174. // Check if we were passed an action to use and if not set it to left.
  175. if (String.IsNullOrEmpty(action))
  176. action = "left";
  177. try { result.Action = action; }
  178. catch { try { result.Action = "normal"; }
  179. catch { try { result.Action = "default"; }
  180. catch {
  181. LogManager.Log(LogLevel.DebugWarning, "ObjectListWidget: No action selected for " + name);
  182. }
  183. }
  184. }
  185. } else { // Not a sprite so it has to be an Image.
  186. try{
  187. result = SpriteManager.CreateFromImage(name);
  188. } catch(Exception) {
  189. result = null;
  190. }
  191. }
  192. return result;
  193. }
  194. /// <summary>Called when a new level is loaded</summary>
  195. private void OnLevelChanged(Level level)
  196. {
  197. if(this.level != null)
  198. this.level.TilesetChanged -= OnTilesetChanged;
  199. if(level != null)
  200. level.TilesetChanged += OnTilesetChanged;
  201. this.level = level;
  202. OnTilesetChanged(level);
  203. }
  204. /// <summary>Called when tileset changes.</summary>
  205. private void OnTilesetChanged(Level level)
  206. {
  207. // Force a reload of object list (so correct objects are loaded).
  208. objectsLoaded = false;
  209. QueueDraw();
  210. }
  211. private void OnButtonPress(object o, ButtonPressEventArgs args)
  212. {
  213. if(args.Event.Button == 1) {
  214. application.SetToolObjects();
  215. Vector MousePos = new Vector((float) args.Event.X,
  216. (float) args.Event.Y);
  217. int? objectNr = GetObjectNrByPosition(MousePos);
  218. int selected;
  219. if (!objectNr.HasValue) {
  220. return ;
  221. } else {
  222. selected = (int)objectNr;
  223. }
  224. if( selected < gameObjectTypes.Count ){
  225. if( SelectedObjectNr != selected ){
  226. SelectedObjectNr = selected;
  227. if( application.CurrentSector != null ) {
  228. Type type = gameObjectTypes[selected];
  229. Sprite Icon = gameObjectSprites[selected];
  230. if(type != null) {
  231. ITool editor = new ObjectCreationTool(application, application.CurrentSector, type, Icon);
  232. application.SetTool(editor);
  233. application.PrintStatus("ObjectListWidget: last selected \"" + gameObjectTypes[selected].Name +"\"");
  234. } else {
  235. ITool editor = new ObjectSelectTool(application, application.CurrentSector);
  236. application.SetTool(editor);
  237. application.PrintStatus("ObjectListWidget: none selected ");
  238. }
  239. }
  240. QueueDraw();
  241. }
  242. }
  243. }
  244. }
  245. private int? GetObjectNrByPosition (Vector MousePos)
  246. {
  247. int row = FirstRow + (int) Math.Floor( MousePos.Y / ROW_HEIGHT );
  248. int column = (int) Math.Floor (MousePos.X / COLUMN_WIDTH);
  249. if( column >= TILES_PER_ROW ){
  250. return null;
  251. } else {
  252. return TILES_PER_ROW * row + column;
  253. }
  254. }
  255. private void OnQueryTooltip (object o, QueryTooltipArgs args)
  256. {
  257. int? objectNr = GetObjectNrByPosition(new Vector(
  258. (float)args.X,
  259. (float)args.Y));
  260. if (objectNr.HasValue) {
  261. int selected = (int)objectNr;
  262. if (selected != LastObjectHovered) {
  263. //HACK: disable tooltip if hovered object changed
  264. LastObjectHovered = selected;
  265. return;
  266. }
  267. args.Tooltip.Text = "";
  268. if (selected < gameObjectTypes.Count) {
  269. Type type = gameObjectTypes[selected];
  270. if (type != null) {
  271. args.Tooltip.Text = type.Name;
  272. args.RetVal = true;
  273. }
  274. }
  275. }
  276. }
  277. private void OnDragBegin(object o, DragBeginArgs args)
  278. {
  279. Gtk.Drag.SetIconWidget( args.Context, SpriteViewWidget.CreateWindow(gameObjectSprites[SelectedObjectNr]), -15, -15);
  280. LogManager.Log(LogLevel.Debug, "Dragstart");
  281. }
  282. private void OnDragEnd(object o, DragEndArgs args)
  283. {
  284. SelectedObjectNr = NONE;
  285. QueueDraw();
  286. }
  287. private void OnDragDataGet (object o, DragDataGetArgs args)
  288. {
  289. if (SelectedObjectNr != NONE){
  290. Atom[] Targets = args.Context.Targets;
  291. foreach (Atom target in Targets){
  292. if (target.Name == "BadguyName") {
  293. if (SelectedObjectNr != 0) { //HACK: don't try to send the name of the arrow (null)
  294. //TODO: Send only badguys into dispensers, no Doors, no Tilemaps..
  295. args.SelectionData.Set (target, 8, System.Text.Encoding.UTF8.GetBytes (gameObjectTypes[SelectedObjectNr].Name.ToLower()));
  296. }
  297. }
  298. //if (target.Name == "GameObject")
  299. //no data transmitted
  300. }
  301. }
  302. }
  303. private void OnScroll(object o, ScrollEventArgs args)
  304. {
  305. if(args.Event.Direction == ScrollDirection.Up && FirstRow > 0 ) {
  306. FirstRow -= 1;
  307. args.RetVal = true;
  308. vadjustment.Value = FirstRow;
  309. QueueDraw();
  310. } else if( args.Event.Direction == ScrollDirection.Down &&
  311. FirstRow + ROWS_PER_PAGE -1 < Math.Floor( (double)gameObjectTypes.Count / (double)TILES_PER_ROW )) {
  312. FirstRow += 1;
  313. args.RetVal = true;
  314. vadjustment.Value = FirstRow;
  315. QueueDraw();
  316. }
  317. }
  318. /// <summary>Vertical Scroll Bar was scrolled</summary>
  319. private void OnVAdjustmentChangedValue(object sender, EventArgs e)
  320. {
  321. FirstRow = (int) vadjustment.Value;
  322. QueueDraw();
  323. }
  324. /// <summary>Calculate TILES_PER_ROW, when we know how wide we are</summary>
  325. private void OnSizeAllocated (object o, SizeAllocatedArgs args)
  326. {
  327. TILES_PER_ROW = args.Allocation.Width / COLUMN_WIDTH;
  328. ROWS_PER_PAGE = args.Allocation.Height / ROW_HEIGHT;
  329. updateScrollbar();
  330. }
  331. /// <summary>adjust the scrollbar</summary>
  332. private void updateScrollbar()
  333. {
  334. double upper = Math.Ceiling( (double)gameObjectTypes.Count / (double)TILES_PER_ROW );
  335. vadjustment.SetBounds(0, upper, 1, ROWS_PER_PAGE-1, ROWS_PER_PAGE);
  336. }
  337. }
  338. /* EOF */