1 // stb_tilemap_editor.h - v0.38 - Sean Barrett - http://nothings.org/stb
2 // placed in the public domain - not copyrighted - first released 2014-09
3 //
4 // Embeddable tilemap editor for C/C++
5 //
6 //
7 // TABLE OF CONTENTS
8 //    FAQ
9 //    How to compile/use the library
10 //    Additional configuration macros
11 //    API documentation
12 //    Info on editing multiple levels
13 //    Revision history
14 //    Todo
15 //    Credits
16 //    License
17 //
18 //
19 // FAQ
20 //
21 //   Q: What counts as a tilemap for this library?
22 //
23 //   A: An array of rectangles, where each rectangle contains a small
24 //      stack of images.
25 //
26 //   Q: What are the limitations?
27 //
28 //   A: Maps are limited to 4096x4096 in dimension.
29 //      Each map square can only contain a stack of at most 32 images.
30 //      A map can only use up to 32768 distinct image tiles.
31 //
32 //   Q: How do I compile this?
33 //
34 //   A: You need to #define several symbols before #including it, but only
35 //      in one file. This will cause all the function definitions to be
36 //      generated in that file. See the "HOW TO COMPILE" section.
37 //
38 //   Q: What advantages does this have over a standalone editor?
39 //
40 //   A: For one, you can integrate the editor into your game so you can
41 //      flip between editing and testing without even switching windows.
42 //      For another, you don't need an XML parser to get at the map data.
43 //
44 //   Q: Can I live-edit my game maps?
45 //
46 //   A: Not really, the editor keeps its own map representation.
47 //
48 //   Q: How do I save and load maps?
49 //
50 //   A: You have to do this yourself. The editor provides serialization
51 //      functions (get & set) for reading and writing the map it holds.
52 //      You can choose whatever format you want to store the map to on
53 //      disk; you just need to provide functions to convert. (For example,
54 //      I actually store the editor's map representation to disk basically
55 //      as-is; then I have a single function that converts from the editor
56 //      map representation to the game representation, which is used both
57 //      to go from editor-to-game and from loaded-map-to-game.)
58 //
59 //   Q: I want to have tiles change appearance based on what's
60 //      adjacent, or other tile-display/substitution trickiness.
61 //
62 //   A: You can do this when you convert from the editor's map
63 //      representation to the game representation, but there's
64 //      no way to show this live in the editor.
65 //
66 //   Q: The editor appears to be put map location (0,0) at the top left?
67 //      I want to use a different coordinate system in my game (e.g. y
68 //      increasing upwards, or origin at the center).
69 //
70 //   A: You can do this when you convert from the editor's map
71 //      representation to the game representation. (Don't forget to
72 //      translate link coordinates as well!)
73 //
74 //   Q: The editor appears to put pixel (0,0) at the top left? I want
75 //      to use a different coordinate system in my game.
76 //
77 //   A: The editor defines an "editor pixel coordinate system" with
78 //      (0,0) at the top left and requires you to display things in
79 //      that coordinate system. You can freely remap those coordinates
80 //      to anything you want on screen.
81 //
82 //   Q: How do I scale the user interface?
83 //
84 //   A: Since you do all the rendering, you can scale up all the rendering
85 //      calls that the library makes to you. If you do, (a) you need
86 //      to also scale up the mouse coordinates, and (b) you may want
87 //      to scale the map display back down so that you're only scaling
88 //      the UI and not everything. See the next question.
89 //
90 //   Q: How do I scale the map display?
91 //
92 //   A: Use stbte_set_spacing() to change the size that the map is displayed
93 //      at. Note that the "callbacks" to draw tiles are used for both drawing
94 //      the map and drawing the tile palette, so that callback may need to
95 //      draw at two different scales. You should choose the scales to match
96 //       You can tell them apart because the
97 //      tile palette gets NULL for the property pointer.
98 //
99 //   Q: How does object editing work?
100 //
101 //   A: One way to think of this is that in the editor, you're placing
102 //      spawners, not objects. Each spawner must be tile-aligned, because
103 //      it's only a tile editor. Each tile (stack of layers) gets
104 //      an associated set of properties, and it's up to you to
105 //      determine what properties should appear for a given tile,
106 //      based on e.g. the spawners that are in it.
107 //
108 //   Q: How are properties themselves handled?
109 //
110 //   A: All properties, regardless of UI behavior, are internally floats.
111 //      Each tile has an array of floats associated with it, which is
112 //      passed back to you when drawing the tiles so you can draw
113 //      objects appropriately modified by the properties.
114 //
115 //   Q: What if I want to have two different objects/spawners in
116 //      one tile, both of which have their own properties?
117 //
118 //   A: Make sure STBTE_MAX_PROPERTIES is large enough for the sum of
119 //      properties in both objects, and then you have to explicitly
120 //      map the property slot #s to the appropriate objects. They'll
121 //      still all appear in a single property panel; there's no way
122 //      to get multiple panels.
123 //
124 //   Q: Can I do one-to-many linking?
125 //
126 //   A: The library only supports one link per tile. However, you
127 //      can have multiple tiles all link to a single tile. So, you
128 //      can fake one-to-many linking by linking in the reverse
129 //      direction.
130 //
131 //   Q: What if I have two objects in the same tile, and they each
132 //      need an independent link? Or I have two kinds of link associated
133 //      with a single object?
134 //
135 //   A: There is no way to do this. (Unless you can reverse one link.)
136 //
137 //   Q: How does cut & paste interact with object properties & links?
138 //
139 //   A: Currently the library has no idea which properties or links
140 //      are associated with which layers of a tile. So currently, the
141 //      library will only copy properties & links if the layer panel
142 //      is set to allow all layers to be copied, OR if you set the
143 //      "props" in the layer panel to "always". Similarly, you can
144 //      set "props" to "none" so it will never copy.
145 //
146 //   Q: What happens if the library gets a memory allocation failure
147 //      while I'm editing? Will I lose my work?
148 //
149 //   A: The library allocates all editor memory when you create
150 //      the tilemap. It allocates a maximally-sized map and a
151 //      fixed-size undo buffer (and the fixed-size copy buffer
152 //      is static), and never allocates memory while it's running.
153 //      So it can't fail due to running out of memory.
154 //
155 //   Q: What happens if the library crashes while I'm editing? Will
156 //      I lose my work?
157 //
158 //   A: Yes. Save often.
159 //
160 //
161 // HOW TO COMPILE
162 //
163 //   This header file contains both the header file and the
164 //   implementation file in one. To create the implementation,
165 //   in one source file define a few symbols first and then
166 //   include this header:
167 //
168 //      #define STB_TILEMAP_EDITOR_IMPLEMENTATION
169 //      // this triggers the implementation
170 //
171 //      void STBTE_DRAW_RECT(int x0, int y0, int x1, int y1, uint color);
172 //      // this must draw a filled rectangle (exclusive on right/bottom)
173 //      // color = (r<<16)|(g<<8)|(b)
174 //
175 //      void STBTE_DRAW_TILE(int x0, int y0,
176 //                    unsigned short id, int highlight, float *data);
177 //      // this draws the tile image identified by 'id' in one of several
178 //      // highlight modes (see STBTE_drawmode_* in the header section);
179 //      // if 'data' is NULL, it's drawing the tile in the palette; if 'data'
180 //      // is not NULL, it's drawing a tile on the map, and that is the data
181 //      // associated with that map tile
182 //
183 //      #include "stb_tilemap_editor.h"
184 //
185 //   Optionally you can define the following functions before the include;
186 //   note these must be macros (but they can just call a function) so
187 //   this library can #ifdef to detect if you've defined them:
188 //
189 //      #define STBTE_PROP_TYPE(int n, short *tiledata, float *params) ...
190 //      // Returns the type of the n'th property of a given tile, which
191 //      // controls how it is edited. Legal types are:
192 //      //     0                    /* no editable property in this slot */
193 //      //     STBTE_PROP_int       /* uses a slider to adjust value     */
194 //      //     STBTE_PROP_float     /* uses a weird multi-axis control   */
195 //      //     STBTE_PROP_bool      /* uses a checkbox to change value   */
196 //      // And you can bitwise-OR in the following flags:
197 //      //     STBTE_PROP_disabled
198 //      // Note that all of these are stored as floats in the param array.
199 //      // The integer slider is limited in precision based on the space
200 //      // available on screen, so for wide-ranged integers you may want
201 //      // to use floats instead.
202 //      //
203 //      // Since the tiledata is passed to you, you can choose which property
204 //      // is bound to that slot based on that data.
205 //      //
206 //      // Changing the type of a parameter does not cause the underlying
207 //      // value to be clamped to the type min/max except when the tile is
208 //      // explicitly selected.
209 //
210 //      #define STBTE_PROP_NAME(int n, short *tiledata, float *params) ...
211 //      // these return a string with the name for slot #n in the float
212 //      // property list for the tile.
213 //
214 //      #define STBTE_PROP_MIN(int n, short *tiledata) ...your code here...
215 //      #define STBTE_PROP_MAX(int n, short *tiledata) ...your code here...
216 //      // These return the allowable range for the property values for
217 //      // the specified slot. It is never called for boolean types.
218 //
219 //      #define STBTE_PROP_FLOAT_SCALE(int n, short *tiledata, float *params)
220 //      // This rescales the float control for a given property; by default
221 //      // left mouse drags add integers, right mouse drags adds fractions,
222 //      // but you can rescale this per-property.
223 //
224 //      #define STBTE_FLOAT_CONTROL_GRANULARITY       ... value ...
225 //      // This returns the number of pixels of mouse motion necessary
226 //      // to advance the object float control. Default is 4
227 //
228 //      #define STBTE_ALLOW_LINK(short *src, float *src_data,  \
229 //                               short *dest, float *dest_data) ...your code...
230 //      // this returns true or false depending on whether you allow a link
231 //      // to be drawn from a tile 'src' to a tile 'dest'. if you don't
232 //      // define this, linking will not be supported
233 //
234 //      #define STBTE_LINK_COLOR(short *src, float *src_data,  \
235 //                               short *dest, float *dest_data) ...your code...
236 //      // return a color encoded as a 24-bit unsigned integer in the
237 //      // form 0xRRGGBB. If you don't define this, default colors will
238 //      // be used.
239 //
240 //
241 //      [[ support for those below is not implemented yet ]]
242 //
243 //      #define STBTE_HITTEST_TILE(x0,y0,id,mx,my)   ...your code here...
244 //      // this returns true or false depending on whether the mouse
245 //      // pointer at mx,my is over (touching) a tile of type 'id'
246 //      // displayed at x0,y0. Normally stb_tilemap_editor just does
247 //      // this hittest based on the tile geometry, but if you have
248 //      // tiles whose images extend out of the tile, you'll need this.
249 //
250 // ADDITIONAL CONFIGURATION
251 //
252 //   The following symbols set static limits which determine how much
253 //   memory will be allocated for the editor. You can override them
254 //   by making similiar definitions, but memory usage will increase.
255 //
256 //      #define STBTE_MAX_TILEMAP_X      200   // max 4096
257 //      #define STBTE_MAX_TILEMAP_Y      200   // max 4096
258 //      #define STBTE_MAX_LAYERS         8     // max 32
259 //      #define STBTE_MAX_CATEGORIES     100
260 //      #define STBTE_UNDO_BUFFER_BYTES  (1 << 24) // 16 MB
261 //      #define STBTE_MAX_COPY           90000  // e.g. 300x300
262 //      #define STBTE_MAX_PROPERTIES     10     // max properties per tile
263 //
264 // API
265 //
266 //   Further documentation appears in the header-file section below.
267 //
268 // EDITING MULTIPLE LEVELS
269 //
270 //   You can only have one active editor instance. To switch between multiple
271 //   levels, you can either store the levels in your own format and copy them
272 //   in and out of the editor format, or you can create multiple stbte_tilemap
273 //   objects and switch between them. The latter has the advantage that each
274 //   stbte_tilemap keeps its own undo state. (The clipboard is global, so
275 //   either approach allows cut&pasting between levels.)
276 //
277 // REVISION HISTORY
278 //   0.38  fix warning
279 //   0.37  fix warning
280 //   0.36  minor compiler support
281 //   0.35  layername button changes
282 //          - layername buttons grow with the layer panel
283 //          - fix stbte_create_map being declared as stbte_create
284 //          - fix declaration of stbte_create_map
285 //   0.30  properties release
286 //          - properties panel for editing user-defined "object" properties
287 //          - can link each tile to one other tile
288 //          - keyboard interface
289 //          - fix eraser tool bug (worked in complex cases, failed in simple)
290 //          - undo/redo tools have visible disabled state
291 //          - tiles on higher layers draw on top of adjacent lower-layer tiles
292 //   0.20  erasable release
293 //          - eraser tool
294 //          - fix bug when pasting into protected layer
295 //          - better color scheme
296 //          - internal-use color picker
297 //   0.10  initial release
298 //
299 // TODO
300 //
301 //   Separate scroll state for each category
302 //   Implement paint bucket
303 //   Support STBTE_HITTEST_TILE above
304 //  ?Cancel drags by clicking other button? - may be fixed
305 //   Finish support for toolbar at side
306 //
307 // CREDITS
308 //
309 //
310 //   Main editor & features
311 //      Sean Barrett
312 //   Additional features:
313 //      Josh Huelsman
314 //   Bugfixes:
315 //      Ryan Whitworth
316 //      Eugene Opalev
317 //
318 // LICENSE
319 //
320 //   See end of file for license information.
321 
322 
323 
324 ///////////////////////////////////////////////////////////////////////
325 //
326 //   HEADER SECTION
327 
328 #ifndef STB_TILEMAP_INCLUDE_STB_TILEMAP_EDITOR_H
329 #define STB_TILEMAP_INCLUDE_STB_TILEMAP_EDITOR_H
330 
331 #ifdef _WIN32
332   #ifndef _CRT_SECURE_NO_WARNINGS
333   #define _CRT_SECURE_NO_WARNINGS
334   #endif
335   #include <stdlib.h>
336   #include <stdio.h>
337 #endif
338 
339 typedef struct stbte_tilemap stbte_tilemap;
340 
341 // these are the drawmodes used in STBTE_DRAW_TILE
342 enum
343 {
344    STBTE_drawmode_deemphasize = -1,
345    STBTE_drawmode_normal      =  0,
346    STBTE_drawmode_emphasize   =  1,
347 };
348 
349 // these are the property types
350 #define STBTE_PROP_none     0
351 #define STBTE_PROP_int      1
352 #define STBTE_PROP_float    2
353 #define STBTE_PROP_bool     3
354 #define STBTE_PROP_disabled 4
355 
356 ////////
357 //
358 // creation
359 //
360 
361 extern stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles);
362 // create an editable tilemap
363 //   map_x      : dimensions of map horizontally (user can change this in editor), <= STBTE_MAX_TILEMAP_X
364 //   map_y      : dimensions of map vertically (user can change this in editor)    <= STBTE_MAX_TILEMAP_Y
365 //   map_layers : number of layers to use (fixed), <= STBTE_MAX_LAYERS
366 //   spacing_x  : initial horizontal distance between left edges of map tiles in stb_tilemap_editor pixels
367 //   spacing_y  : initial vertical distance between top edges of map tiles in stb_tilemap_editor pixels
368 //   max_tiles  : maximum number of tiles that can defined
369 //
370 // If insufficient memory, returns NULL
371 
372 extern void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layermask, const char * category);
373 // call this repeatedly for each tile to install the tile definitions into the editable tilemap
374 //   tm        : tilemap created by stbte_create_map
375 //   id        : unique identifier for each tile, 0 <= id < 32768
376 //   layermask : bitmask of which layers tile is allowed on: 1 = layer 0, 255 = layers 0..7
377 //               (note that onscreen, the editor numbers the layers from 1 not 0)
378 //               layer 0 is the furthest back, layer 1 is just in front of layer 0, etc
379 //   category  : which category this tile is grouped in
380 
381 extern void stbte_set_display(int x0, int y0, int x1, int y1);
382 // call this once to set the size; if you resize, call it again
383 
384 
385 /////////
386 //
387 // every frame
388 //
389 
390 extern void stbte_draw(stbte_tilemap *tm);
391 
392 extern void stbte_tick(stbte_tilemap *tm, float time_in_seconds_since_last_frame);
393 
394 ////////////
395 //
396 //  user input
397 //
398 
399 // if you're using SDL, call the next function for SDL_MOUSEMOVE, SDL_MOUSEBUTTON, SDL_MOUSEWHEEL;
400 // the transformation lets you scale from SDL mouse coords to stb_tilemap_editor coords
401 extern void stbte_mouse_sdl(stbte_tilemap *tm, const void *sdl_event, float xscale, float yscale, int xoffset, int yoffset);
402 
403 // otherwise, hook these up explicitly:
404 extern void stbte_mouse_move(stbte_tilemap *tm, int x, int y, int shifted, int scrollkey);
405 extern void stbte_mouse_button(stbte_tilemap *tm, int x, int y, int right, int down, int shifted, int scrollkey);
406 extern void stbte_mouse_wheel(stbte_tilemap *tm, int x, int y, int vscroll);
407 
408 // for keyboard, define your own mapping from keys to the following actions.
409 // this is totally optional, as all features are accessible with the mouse
410 enum stbte_action
411 {
412    STBTE_tool_select,
413    STBTE_tool_brush,
414    STBTE_tool_erase,
415    STBTE_tool_rectangle,
416    STBTE_tool_eyedropper,
417    STBTE_tool_link,
418    STBTE_act_toggle_grid,
419    STBTE_act_toggle_links,
420    STBTE_act_undo,
421    STBTE_act_redo,
422    STBTE_act_cut,
423    STBTE_act_copy,
424    STBTE_act_paste,
425    STBTE_scroll_left,
426    STBTE_scroll_right,
427    STBTE_scroll_up,
428    STBTE_scroll_down,
429 };
430 extern void stbte_action(stbte_tilemap *tm, enum stbte_action act);
431 
432 ////////////////
433 //
434 //  save/load
435 //
436 //  There is no editor file format. You have to save and load the data yourself
437 //  through the following functions. You can also use these functions to get the
438 //  data to generate game-formatted levels directly. (But make sure you save
439 //  first! You may also want to autosave to a temp file periodically, etc etc.)
440 
441 #define STBTE_EMPTY    -1
442 
443 extern void stbte_get_dimensions(stbte_tilemap *tm, int *max_x, int *max_y);
444 // get the dimensions of the level, since the user can change them
445 
446 extern short* stbte_get_tile(stbte_tilemap *tm, int x, int y);
447 // returns an array of shorts that is 'map_layers' in length. each short is
448 // either one of the tile_id values from define_tile, or STBTE_EMPTY.
449 
450 extern float *stbte_get_properties(stbte_tilemap *tm, int x, int y);
451 // get the property array associated with the tile at x,y. this is an
452 // array of floats that is STBTE_MAX_PROPERTIES in length; you have to
453 // interpret the slots according to the semantics you've chosen
454 
455 extern void stbte_get_link(stbte_tilemap *tm, int x, int y, int *destx, int *desty);
456 // gets the link associated with the tile at x,y.
457 
458 extern void stbte_set_dimensions(stbte_tilemap *tm, int max_x, int max_y);
459 // set the dimensions of the level, overrides previous stbte_create_map()
460 // values or anything the user has changed
461 
462 extern void stbte_clear_map(stbte_tilemap *tm);
463 // clears the map, including the region outside the defined region, so if the
464 // user expands the map, they won't see garbage there
465 
466 extern void stbte_set_tile(stbte_tilemap *tm, int x, int y, int layer, signed short tile);
467 // tile is your tile_id from define_tile, or STBTE_EMPTY
468 
469 extern void stbte_set_property(stbte_tilemap *tm, int x, int y, int n, float val);
470 // set the value of the n'th slot of the tile at x,y
471 
472 extern void stbte_set_link(stbte_tilemap *tm, int x, int y, int destx, int desty);
473 // set a link going from x,y to destx,desty. to force no link,
474 // use destx=desty=-1
475 
476 ////////
477 //
478 // optional
479 //
480 
481 extern void stbte_set_background_tile(stbte_tilemap *tm, short id);
482 // selects the tile to fill the bottom layer with and used to clear bottom tiles to;
483 // should be same ID as
484 
485 extern void stbte_set_sidewidths(int left, int right);
486 // call this once to set the left & right side widths. don't call
487 // it again since the user can change it
488 
489 extern void stbte_set_spacing(stbte_tilemap *tm, int spacing_x, int spacing_y, int palette_spacing_x, int palette_spacing_y);
490 // call this to set the spacing of map tiles and the spacing of palette tiles.
491 // if you rescale your display, call it again (e.g. you can implement map zooming yourself)
492 
493 extern void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layername);
494 // sets a string name for your layer that shows in the layer selector. note that this
495 // makes the layer selector wider. 'layer' is from 0..(map_layers-1)
496 
497 #endif
498 
499 #ifdef STB_TILEMAP_EDITOR_IMPLEMENTATION
500 
501 #ifndef STBTE_ASSERT
502 #define STBTE_ASSERT assert
503 #include <assert.h>
504 #endif
505 
506 #ifdef _MSC_VER
507 #define STBTE__NOTUSED(v)  (void)(v)
508 #else
509 #define STBTE__NOTUSED(v)  (void)sizeof(v)
510 #endif
511 
512 #ifndef STBTE_MAX_TILEMAP_X
513 #define STBTE_MAX_TILEMAP_X      200
514 #endif
515 
516 #ifndef STBTE_MAX_TILEMAP_Y
517 #define STBTE_MAX_TILEMAP_Y      200
518 #endif
519 
520 #ifndef STBTE_MAX_LAYERS
521 #define STBTE_MAX_LAYERS         8
522 #endif
523 
524 #ifndef STBTE_MAX_CATEGORIES
525 #define STBTE_MAX_CATEGORIES     100
526 #endif
527 
528 #ifndef STBTE_MAX_COPY
529 #define STBTE_MAX_COPY           65536
530 #endif
531 
532 #ifndef STBTE_UNDO_BUFFER_BYTES
533 #define STBTE_UNDO_BUFFER_BYTES  (1 << 24) // 16 MB
534 #endif
535 
536 #ifndef STBTE_PROP_TYPE
537 #define STBTE__NO_PROPS
538 #define STBTE_PROP_TYPE(n,td,tp)   0
539 #endif
540 
541 #ifndef STBTE_PROP_NAME
542 #define STBTE_PROP_NAME(n,td,tp)  ""
543 #endif
544 
545 #ifndef STBTE_MAX_PROPERTIES
546 #define STBTE_MAX_PROPERTIES           10
547 #endif
548 
549 #ifndef STBTE_PROP_MIN
550 #define STBTE_PROP_MIN(n,td,tp)  0
551 #endif
552 
553 #ifndef STBTE_PROP_MAX
554 #define STBTE_PROP_MAX(n,td,tp)  100.0
555 #endif
556 
557 #ifndef STBTE_PROP_FLOAT_SCALE
558 #define STBTE_PROP_FLOAT_SCALE(n,td,tp)  1   // default scale size
559 #endif
560 
561 #ifndef STBTE_FLOAT_CONTROL_GRANULARITY
562 #define STBTE_FLOAT_CONTROL_GRANULARITY 4
563 #endif
564 
565 
566 #define STBTE__UNDO_BUFFER_COUNT  (STBTE_UNDO_BUFFER_BYTES>>1)
567 
568 #if STBTE_MAX_TILEMAP_X > 4096 || STBTE_MAX_TILEMAP_Y > 4096
569 #error "Maximum editable map size is 4096 x 4096"
570 #endif
571 #if STBTE_MAX_LAYERS > 32
572 #error "Maximum layers allowed is 32"
573 #endif
574 #if STBTE_UNDO_BUFFER_COUNT & (STBTE_UNDO_BUFFER_COUNT-1)
575 #error "Undo buffer size must be a power of 2"
576 #endif
577 
578 #if STBTE_MAX_PROPERTIES == 0
579 #define STBTE__NO_PROPS
580 #endif
581 
582 #ifdef STBTE__NO_PROPS
583 #undef STBTE_MAX_PROPERTIES
584 #define STBTE_MAX_PROPERTIES 1  // so we can declare arrays
585 #endif
586 
587 typedef struct
588 {
589    short x,y;
590 } stbte__link;
591 
592 enum
593 {
594    STBTE__base,
595    STBTE__outline,
596    STBTE__text,
597 
598    STBTE__num_color_aspects,
599 };
600 
601 enum
602 {
603    STBTE__idle,
604    STBTE__over,
605    STBTE__down,
606    STBTE__over_down,
607    STBTE__selected,
608    STBTE__selected_over,
609    STBTE__disabled,
610    STBTE__num_color_states,
611 };
612 
613 enum
614 {
615    STBTE__cexpander,
616    STBTE__ctoolbar,
617    STBTE__ctoolbar_button,
618    STBTE__cpanel,
619    STBTE__cpanel_sider,
620    STBTE__cpanel_sizer,
621    STBTE__cscrollbar,
622    STBTE__cmapsize,
623    STBTE__clayer_button,
624    STBTE__clayer_hide,
625    STBTE__clayer_lock,
626    STBTE__clayer_solo,
627    STBTE__ccategory_button,
628 
629    STBTE__num_color_modes,
630 };
631 
632 #ifdef STBTE__COLORPICKER
633 static char *stbte__color_names[] =
634 {
635    "expander", "toolbar", "tool button", "panel",
636    "panel c1", "panel c2", "scollbar", "map button",
637    "layer", "hide", "lock", "solo",
638    "category",
639 };
640 #endif // STBTE__COLORPICKER
641 
642       // idle,    over,     down,    over&down, selected, sel&over, disabled
643 static int stbte__color_table[STBTE__num_color_modes][STBTE__num_color_aspects][STBTE__num_color_states] =
644 {
645    {
646       { 0x000000, 0x84987c, 0xdcdca8, 0xdcdca8, 0x40c040, 0x60d060, 0x505050, },
647       { 0xa4b090, 0xe0ec80, 0xffffc0, 0xffffc0, 0x80ff80, 0x80ff80, 0x606060, },
648       { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
649    }, {
650       { 0x808890, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, },
651       { 0x605860, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, },
652       { 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, },
653    }, {
654       { 0x3c5068, 0x7088a8, 0x647488, 0x94b4dc, 0x8890c4, 0x9caccc, 0x404040, },
655       { 0x889cb8, 0x889cb8, 0x889cb8, 0x889cb8, 0x84c4e8, 0xacc8ff, 0x0c0c08, },
656       { 0xbcc4cc, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x707074, },
657    }, {
658       { 0x403848, 0x403010, 0x403010, 0x403010, 0x403010, 0x403010, 0x303024, },
659       { 0x68546c, 0xc08040, 0xc08040, 0xc08040, 0xc08040, 0xc08040, 0x605030, },
660       { 0xf4e4ff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
661    }, {
662       { 0xb4b04c, 0xacac60, 0xc0ffc0, 0xc0ffc0, 0x40c040, 0x60d060, 0x505050, },
663       { 0xa0a04c, 0xd0d04c, 0xffff80, 0xffff80, 0x80ff80, 0x80ff80, 0x606060, },
664       { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
665    }, {
666       { 0x40c440, 0x60d060, 0xc0ffc0, 0xc0ffc0, 0x40c040, 0x60d060, 0x505050, },
667       { 0x40c040, 0x80ff80, 0x80ff80, 0x80ff80, 0x80ff80, 0x80ff80, 0x606060, },
668       { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
669    }, {
670       { 0x9090ac, 0xa0a0b8, 0xbcb8cc, 0xbcb8cc, 0x909040, 0x909040, 0x909040, },
671       { 0xa0a0b8, 0xb0b4d0, 0xa0a0b8, 0xa0a0b8, 0xa0a050, 0xa0a050, 0xa0a050, },
672       { 0x808088, 0x808030, 0x808030, 0x808030, 0x808030, 0x808030, 0x808030, },
673    }, {
674       { 0x704c70, 0x885c8c, 0x9c68a4, 0xb870bc, 0xb490bc, 0xb490bc, 0x302828, },
675       { 0x646064, 0xcca8d4, 0xc060c0, 0xa07898, 0xe0b8e0, 0xe0b8e0, 0x403838, },
676       { 0xdccce4, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
677    }, {
678       { 0x704c70, 0x885c8c, 0x9c68a4, 0xb870bc, 0xb490bc, 0xb490bc, 0x302828, },
679       { 0xb09cb4, 0xcca8d4, 0xc060c0, 0xa07898, 0xe0b8e0, 0xe0b8e0, 0x403838, },
680       { 0xdccce4, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
681    }, {
682       { 0x646494, 0x888cb8, 0xb0b0b0, 0xb0b0cc, 0x9c9cf4, 0x8888b0, 0x50506c, },
683       { 0x9090a4, 0xb0b4d4, 0xb0b0dc, 0xb0b0cc, 0xd0d0fc, 0xd0d4f0, 0x606060, },
684       { 0xb4b4d4, 0xe4e4ff, 0xffffff, 0xffffff, 0xe0e4ff, 0xececff, 0x909090, },
685    }, {
686       { 0x646444, 0x888c64, 0xb0b0b0, 0xb0b088, 0xaca858, 0x88886c, 0x505050, },
687       { 0x88886c, 0xb0b490, 0xb0b0b0, 0xb0b088, 0xd8d898, 0xd0d4b0, 0x606060, },
688       { 0xb4b49c, 0xffffd8, 0xffffff, 0xffffd4, 0xffffdc, 0xffffcc, 0x909090, },
689    }, {
690       { 0x906464, 0xb48c8c, 0xd4b0b0, 0xdcb0b0, 0xff9c9c, 0xc88888, 0x505050, },
691       { 0xb47c80, 0xd4b4b8, 0xc4a8a8, 0xdcb0b0, 0xffc0c0, 0xfce8ec, 0x606060, },
692       { 0xe0b4b4, 0xffdcd8, 0xffd8d4, 0xffe0e4, 0xffece8, 0xffffff, 0x909090, },
693    }, {
694       { 0x403848, 0x403848, 0x403848, 0x886894, 0x7c80c8, 0x7c80c8, 0x302828, },
695       { 0x403848, 0x403848, 0x403848, 0x403848, 0x7c80c8, 0x7c80c8, 0x403838, },
696       { 0xc8c4c8, 0xffffff, 0xffffff, 0xffffff, 0xe8e8ec, 0xffffff, 0x909090, },
697    },
698 };
699 
700 #define STBTE_COLOR_TILEMAP_BACKGROUND      0x000000
701 #define STBTE_COLOR_TILEMAP_BORDER          0x203060
702 #define STBTE_COLOR_TILEMAP_HIGHLIGHT       0xffffff
703 #define STBTE_COLOR_GRID                    0x404040
704 #define STBTE_COLOR_SELECTION_OUTLINE1      0xdfdfdf
705 #define STBTE_COLOR_SELECTION_OUTLINE2      0x303030
706 #define STBTE_COLOR_TILEPALETTE_OUTLINE     0xffffff
707 #define STBTE_COLOR_TILEPALETTE_BACKGROUND  0x000000
708 
709 #ifndef STBTE_LINK_COLOR
710 #define STBTE_LINK_COLOR(src,sp,dest,dp)    0x5030ff
711 #endif
712 
713 #ifndef STBTE_LINK_COLOR_DRAWING
714 #define STBTE_LINK_COLOR_DRAWING            0xff40ff
715 #endif
716 
717 #ifndef STBTE_LINK_COLOR_DISALLOWED
718 #define STBTE_LINK_COLOR_DISALLOWED         0x602060
719 #endif
720 
721 
722 // disabled, selected, down, over
723 static unsigned char stbte__state_to_index[2][2][2][2] =
724 {
725    {
726       { { STBTE__idle    , STBTE__over          }, { STBTE__down    , STBTE__over_down }, },
727       { { STBTE__selected, STBTE__selected_over }, { STBTE__down    , STBTE__over_down }, },
728    },{
729       { { STBTE__disabled, STBTE__disabled      }, { STBTE__disabled, STBTE__disabled  }, },
730       { { STBTE__selected, STBTE__selected_over }, { STBTE__disabled, STBTE__disabled  }, },
731    }
732 };
733 #define STBTE__INDEX_FOR_STATE(disable,select,down,over) stbte__state_to_index[disable][select][down][over]
734 #define STBTE__INDEX_FOR_ID(id,disable,select) STBTE__INDEX_FOR_STATE(disable,select,STBTE__IS_ACTIVE(id),STBTE__IS_HOT(id))
735 
736 #define STBTE__FONT_HEIGHT    9
737 static short stbte__font_offset[95+16];
738 static short stbte__fontdata[769] =
739 {
740    4,9,6,9,9,9,9,8,9,8,4,9,7,7,7,7,4,2,6,8,6,6,7,3,4,4,8,6,3,6,2,6,6,6,6,6,6,
741    6,6,6,6,6,2,3,5,4,5,6,6,6,6,6,6,6,6,6,6,6,6,7,6,7,7,7,6,7,6,6,6,6,7,7,6,6,
742    6,4,6,4,7,7,3,6,6,5,6,6,5,6,6,4,5,6,4,7,6,6,6,6,6,6,6,6,6,7,6,6,6,5,2,5,8,
743    0,0,0,0,2,253,130,456,156,8,72,184,64,2,125,66,64,160,64,146,511,146,146,
744    511,146,146,511,146,511,257,341,297,341,297,341,257,511,16,56,124,16,16,16,
745    124,56,16,96,144,270,261,262,136,80,48,224,192,160,80,40,22,14,15,3,448,496,
746    496,240,232,20,10,5,2,112,232,452,450,225,113,58,28,63,30,60,200,455,257,
747    257,0,0,0,257,257,455,120,204,132,132,159,14,4,4,14,159,132,132,204,120,8,
748    24,56,120,56,24,8,32,48,56,60,56,48,32,0,0,0,0,111,111,7,7,0,0,7,7,34,127,
749    127,34,34,127,127,34,36,46,107,107,58,18,99,51,24,12,102,99,48,122,79,93,
750    55,114,80,4,7,3,62,127,99,65,65,99,127,62,8,42,62,28,28,62,42,8,8,8,62,62,
751    8,8,128,224,96,8,8,8,8,8,8,96,96,96,48,24,12,6,3,62,127,89,77,127,62,64,66,
752    127,127,64,64,98,115,89,77,71,66,33,97,73,93,119,35,24,28,22,127,127,16,39,
753    103,69,69,125,57,62,127,73,73,121,48,1,1,113,121,15,7,54,127,73,73,127,54,
754    6,79,73,105,63,30,54,54,128,246,118,8,28,54,99,65,20,20,20,20,65,99,54,28,
755    8,2,3,105,109,7,2,30,63,33,45,47,46,124,126,19,19,126,124,127,127,73,73,127,
756    54,62,127,65,65,99,34,127,127,65,99,62,28,127,127,73,73,73,65,127,127,9,9,
757    9,1,62,127,65,73,121,121,127,127,8,8,127,127,65,65,127,127,65,65,32,96,64,
758    64,127,63,127,127,8,28,54,99,65,127,127,64,64,64,64,127,127,6,12,6,127,127,
759    127,127,6,12,24,127,127,62,127,65,65,65,127,62,127,127,9,9,15,6,62,127,65,
760    81,49,127,94,127,127,9,25,127,102,70,79,73,73,121,49,1,1,127,127,1,1,63,127,
761    64,64,127,63,15,31,48,96,48,31,15,127,127,48,24,48,127,127,99,119,28,28,119,
762    99,7,15,120,120,15,7,97,113,89,77,71,67,127,127,65,65,3,6,12,24,48,96,65,
763    65,127,127,8,12,6,3,6,12,8,64,64,64,64,64,64,64,3,7,4,32,116,84,84,124,120,
764    127,127,68,68,124,56,56,124,68,68,68,56,124,68,68,127,127,56,124,84,84,92,
765    24,8,124,126,10,10,56,380,324,324,508,252,127,127,4,4,124,120,72,122,122,
766    64,256,256,256,506,250,126,126,16,56,104,64,66,126,126,64,124,124,24,56,28,
767    124,120,124,124,4,4,124,120,56,124,68,68,124,56,508,508,68,68,124,56,56,124,
768    68,68,508,508,124,124,4,4,12,8,72,92,84,84,116,36,4,4,62,126,68,68,60,124,
769    64,64,124,124,28,60,96,96,60,28,28,124,112,56,112,124,28,68,108,56,56,108,
770    68,284,316,352,320,508,252,68,100,116,92,76,68,8,62,119,65,65,127,127,65,
771    65,119,62,8,16,24,12,12,24,24,12,4,
772 };
773 
774 typedef struct
775 {
776    short id;
777    unsigned short category_id;
778    char *category;
779    unsigned int layermask;
780 } stbte__tileinfo;
781 
782 #define MAX_LAYERMASK    (1 << (8*sizeof(unsigned int)))
783 
784 typedef short stbte__tiledata;
785 
786 #define STBTE__NO_TILE   -1
787 
788 enum
789 {
790    STBTE__panel_toolbar,
791    STBTE__panel_colorpick,
792    STBTE__panel_info,
793    STBTE__panel_layers,
794    STBTE__panel_props,
795    STBTE__panel_categories,
796    STBTE__panel_tiles,
797 
798    STBTE__num_panel,
799 };
800 
801 enum
802 {
803    STBTE__side_left,
804    STBTE__side_right,
805    STBTE__side_top,
806    STBTE__side_bottom,
807 };
808 
809 enum
810 {
811    STBTE__tool_select,
812    STBTE__tool_brush,
813    STBTE__tool_erase,
814    STBTE__tool_rect,
815    STBTE__tool_eyedrop,
816    STBTE__tool_fill,
817    STBTE__tool_link,
818 
819    STBTE__tool_showgrid,
820    STBTE__tool_showlinks,
821 
822    STBTE__tool_undo,
823    STBTE__tool_redo,
824    // copy/cut/paste aren't included here because they're displayed differently
825 
826    STBTE__num_tool,
827 };
828 
829 // icons are stored in the 0-31 range of ASCII in the font
830 static int toolchar[] = { 26,24,25,20,23,22,18, 19,17, 29,28, };
831 
832 enum
833 {
834    STBTE__propmode_default,
835    STBTE__propmode_always,
836    STBTE__propmode_never,
837 };
838 
839 enum
840 {
841    STBTE__paint,
842 
843    // from here down does hittesting
844    STBTE__tick,
845    STBTE__mousemove,
846    STBTE__mousewheel,
847    STBTE__leftdown,
848    STBTE__leftup,
849    STBTE__rightdown,
850    STBTE__rightup,
851 };
852 
853 typedef struct
854 {
855    int expanded, mode;
856    int delta_height;     // number of rows they've requested for this
857    int side;
858    int width,height;
859    int x0,y0;
860 } stbte__panel;
861 
862 typedef struct
863 {
864    int x0,y0,x1,y1,color;
865 } stbte__colorrect;
866 
867 #define STBTE__MAX_DELAYRECT 256
868 
869 typedef struct
870 {
871    int tool, active_event;
872    int active_id, hot_id, next_hot_id;
873    int event;
874    int mx,my, dx,dy;
875    int ms_time;
876    int shift, scrollkey;
877    int initted;
878    int side_extended[2];
879    stbte__colorrect delayrect[STBTE__MAX_DELAYRECT];
880    int delaycount;
881    int show_grid, show_links;
882    int brush_state; // used to decide which kind of erasing
883    int eyedrop_x, eyedrop_y, eyedrop_last_layer;
884    int pasting, paste_x, paste_y;
885    int scrolling, start_x, start_y;
886    int last_mouse_x, last_mouse_y;
887    int accum_x, accum_y;
888    int linking;
889    int dragging;
890    int drag_x, drag_y, drag_w, drag_h;
891    int drag_offx, drag_offy, drag_dest_x, drag_dest_y;
892    int undoing;
893    int has_selection, select_x0, select_y0, select_x1, select_y1;
894    int sx,sy;
895    int x0,y0,x1,y1, left_width, right_width; // configurable widths
896    float alert_timer;
897    const char *alert_msg;
898    float dt;
899    stbte__panel panel[STBTE__num_panel];
900    short copybuffer[STBTE_MAX_COPY][STBTE_MAX_LAYERS];
901    float copyprops[STBTE_MAX_COPY][STBTE_MAX_PROPERTIES];
902 #ifdef STBTE_ALLOW_LINK
903    stbte__link copylinks[STBTE_MAX_COPY];
904 #endif
905    int copy_src_x, copy_src_y;
906    stbte_tilemap *copy_src;
907    int copy_width,copy_height,has_copy,copy_has_props;
908 } stbte__ui_t;
909 
910 // there's only one UI system at a time, so we can globalize this
911 static stbte__ui_t stbte__ui = { STBTE__tool_brush, 0 };
912 
913 #define STBTE__INACTIVE()     (stbte__ui.active_id == 0)
914 #define STBTE__IS_ACTIVE(id)  (stbte__ui.active_id == (id))
915 #define STBTE__IS_HOT(id)     (stbte__ui.hot_id    == (id))
916 
917 #define STBTE__BUTTON_HEIGHT            (STBTE__FONT_HEIGHT + 2 * STBTE__BUTTON_INTERNAL_SPACING)
918 #define STBTE__BUTTON_INTERNAL_SPACING  (2 + (STBTE__FONT_HEIGHT>>4))
919 
920 typedef struct
921 {
922    const char *name;
923    int locked;
924    int hidden;
925 } stbte__layer;
926 
927 enum
928 {
929    STBTE__unlocked,
930    STBTE__protected,
931    STBTE__locked,
932 };
933 
934 struct stbte_tilemap
935 {
936     stbte__tiledata data[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X][STBTE_MAX_LAYERS];
937     float props[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X][STBTE_MAX_PROPERTIES];
938     #ifdef STBTE_ALLOW_LINK
939     stbte__link link[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X];
940     int linkcount[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X];
941     #endif
942     int max_x, max_y, num_layers;
943     int spacing_x, spacing_y;
944     int palette_spacing_x, palette_spacing_y;
945     int scroll_x,scroll_y;
946     int cur_category, cur_tile, cur_layer;
947     char *categories[STBTE_MAX_CATEGORIES];
948     int num_categories, category_scroll;
949     stbte__tileinfo *tiles;
950     int num_tiles, max_tiles, digits;
951     unsigned char undo_available_valid;
952     unsigned char undo_available;
953     unsigned char redo_available;
954     unsigned char padding;
955     int cur_palette_count;
956     int palette_scroll;
957     int tileinfo_dirty;
958     stbte__layer layerinfo[STBTE_MAX_LAYERS];
959     int has_layer_names;
960     int layername_width;
961     int layer_scroll;
962     int propmode;
963     int solo_layer;
964     int undo_pos, undo_len, redo_len;
965     short background_tile;
966     unsigned char id_in_use[32768>>3];
967     short *undo_buffer;
968 };
969 
970 static char *default_category = "[unassigned]";
971 
stbte__init_gui(void)972 static void stbte__init_gui(void)
973 {
974    int i,n;
975    stbte__ui.initted = 1;
976    // init UI state
977    stbte__ui.show_links = 1;
978    for (i=0; i < STBTE__num_panel; ++i) {
979       stbte__ui.panel[i].expanded     = 1; // visible if not autohidden
980       stbte__ui.panel[i].delta_height = 0;
981       stbte__ui.panel[i].side         = STBTE__side_left;
982    }
983    stbte__ui.panel[STBTE__panel_toolbar  ].side = STBTE__side_top;
984    stbte__ui.panel[STBTE__panel_colorpick].side = STBTE__side_right;
985 
986    if (stbte__ui.left_width == 0)
987       stbte__ui.left_width = 80;
988    if (stbte__ui.right_width == 0)
989       stbte__ui.right_width = 80;
990 
991    // init font
992    n=95+16;
993    for (i=0; i < 95+16; ++i) {
994       stbte__font_offset[i] = n;
995       n += stbte__fontdata[i];
996    }
997 }
998 
stbte_create_map(int map_x,int map_y,int map_layers,int spacing_x,int spacing_y,int max_tiles)999 stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles)
1000 {
1001    int i;
1002    stbte_tilemap *tm;
1003    STBTE_ASSERT(map_layers >= 0 && map_layers <= STBTE_MAX_LAYERS);
1004    STBTE_ASSERT(map_x >= 0 && map_x <= STBTE_MAX_TILEMAP_X);
1005    STBTE_ASSERT(map_y >= 0 && map_y <= STBTE_MAX_TILEMAP_Y);
1006    if (map_x < 0 || map_y < 0 || map_layers < 0 ||
1007        map_x > STBTE_MAX_TILEMAP_X || map_y > STBTE_MAX_TILEMAP_Y || map_layers > STBTE_MAX_LAYERS)
1008       return NULL;
1009 
1010    if (!stbte__ui.initted)
1011       stbte__init_gui();
1012 
1013    tm = (stbte_tilemap *) malloc(sizeof(*tm) + sizeof(*tm->tiles) * max_tiles + STBTE_UNDO_BUFFER_BYTES);
1014    if (tm == NULL)
1015       return NULL;
1016 
1017    tm->tiles = (stbte__tileinfo *) (tm+1);
1018    tm->undo_buffer = (short *) (tm->tiles + max_tiles);
1019    tm->num_layers = map_layers;
1020    tm->max_x = map_x;
1021    tm->max_y = map_y;
1022    tm->spacing_x = spacing_x;
1023    tm->spacing_y = spacing_y;
1024    tm->scroll_x = 0;
1025    tm->scroll_y = 0;
1026    tm->palette_scroll = 0;
1027    tm->palette_spacing_x = spacing_x+1;
1028    tm->palette_spacing_y = spacing_y+1;
1029    tm->cur_category = -1;
1030    tm->cur_tile = 0;
1031    tm->solo_layer = -1;
1032    tm->undo_len = 0;
1033    tm->redo_len = 0;
1034    tm->undo_pos = 0;
1035    tm->category_scroll = 0;
1036    tm->layer_scroll = 0;
1037    tm->propmode = 0;
1038    tm->has_layer_names = 0;
1039    tm->layername_width = 0;
1040    tm->undo_available_valid = 0;
1041 
1042    for (i=0; i < tm->num_layers; ++i) {
1043       tm->layerinfo[i].hidden = 0;
1044       tm->layerinfo[i].locked = STBTE__unlocked;
1045       tm->layerinfo[i].name   = 0;
1046    }
1047 
1048    tm->background_tile = STBTE__NO_TILE;
1049    stbte_clear_map(tm);
1050 
1051    tm->max_tiles = max_tiles;
1052    tm->num_tiles = 0;
1053    for (i=0; i < 32768/8; ++i)
1054       tm->id_in_use[i] = 0;
1055    tm->tileinfo_dirty = 1;
1056    return tm;
1057 }
1058 
stbte_set_background_tile(stbte_tilemap * tm,short id)1059 void stbte_set_background_tile(stbte_tilemap *tm, short id)
1060 {
1061    int i;
1062    STBTE_ASSERT(id >= -1 && id < 32768);
1063    if (id >= 32768 || id < -1)
1064       return;
1065    for (i=0; i < STBTE_MAX_TILEMAP_X * STBTE_MAX_TILEMAP_Y; ++i)
1066       if (tm->data[0][i][0] == -1)
1067          tm->data[0][i][0] = id;
1068    tm->background_tile = id;
1069 }
1070 
stbte_set_spacing(stbte_tilemap * tm,int spacing_x,int spacing_y,int palette_spacing_x,int palette_spacing_y)1071 void stbte_set_spacing(stbte_tilemap *tm, int spacing_x, int spacing_y, int palette_spacing_x, int palette_spacing_y)
1072 {
1073    tm->spacing_x = spacing_x;
1074    tm->spacing_y = spacing_y;
1075    tm->palette_spacing_x = palette_spacing_x;
1076    tm->palette_spacing_y = palette_spacing_y;
1077 }
1078 
stbte_set_sidewidths(int left,int right)1079 void stbte_set_sidewidths(int left, int right)
1080 {
1081    stbte__ui.left_width  = left;
1082    stbte__ui.right_width = right;
1083 }
1084 
stbte_set_display(int x0,int y0,int x1,int y1)1085 void stbte_set_display(int x0, int y0, int x1, int y1)
1086 {
1087    stbte__ui.x0 = x0;
1088    stbte__ui.y0 = y0;
1089    stbte__ui.x1 = x1;
1090    stbte__ui.y1 = y1;
1091 }
1092 
stbte_define_tile(stbte_tilemap * tm,unsigned short id,unsigned int layermask,const char * category_c)1093 void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layermask, const char * category_c)
1094 {
1095    char *category = (char *) category_c;
1096    STBTE_ASSERT(id < 32768);
1097    STBTE_ASSERT(tm->num_tiles < tm->max_tiles);
1098    STBTE_ASSERT((tm->id_in_use[id>>3]&(1<<(id&7))) == 0);
1099    if (id >= 32768 || tm->num_tiles >= tm->max_tiles || (tm->id_in_use[id>>3]&(1<<(id&7))))
1100       return;
1101 
1102    if (category == NULL)
1103       category = (char*) default_category;
1104    tm->id_in_use[id>>3] |= 1 << (id&7);
1105    tm->tiles[tm->num_tiles].category    = category;
1106    tm->tiles[tm->num_tiles].id        = id;
1107    tm->tiles[tm->num_tiles].layermask = layermask;
1108    ++tm->num_tiles;
1109    tm->tileinfo_dirty = 1;
1110 }
1111 
1112 static int stbte__text_width(const char *str);
1113 
stbte_set_layername(stbte_tilemap * tm,int layer,const char * layername)1114 void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layername)
1115 {
1116    STBTE_ASSERT(layer >= 0 && layer < tm->num_layers);
1117    if (layer >= 0 && layer < tm->num_layers) {
1118       int width;
1119       tm->layerinfo[layer].name = layername;
1120       tm->has_layer_names = 1;
1121       width = stbte__text_width(layername);
1122       tm->layername_width = (width > tm->layername_width ? width : tm->layername_width);
1123    }
1124 }
1125 
stbte_get_dimensions(stbte_tilemap * tm,int * max_x,int * max_y)1126 void stbte_get_dimensions(stbte_tilemap *tm, int *max_x, int *max_y)
1127 {
1128    *max_x = tm->max_x;
1129    *max_y = tm->max_y;
1130 }
1131 
stbte_get_tile(stbte_tilemap * tm,int x,int y)1132 short* stbte_get_tile(stbte_tilemap *tm, int x, int y)
1133 {
1134    STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
1135    if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y)
1136       return NULL;
1137    return tm->data[y][x];
1138 }
1139 
stbte_get_properties(stbte_tilemap * tm,int x,int y)1140 float *stbte_get_properties(stbte_tilemap *tm, int x, int y)
1141 {
1142    STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
1143    if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y)
1144       return NULL;
1145    return tm->props[y][x];
1146 }
1147 
stbte_get_link(stbte_tilemap * tm,int x,int y,int * destx,int * desty)1148 void stbte_get_link(stbte_tilemap *tm, int x, int y, int *destx, int *desty)
1149 {
1150    int gx=-1,gy=-1;
1151    STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
1152 #ifdef STBTE_ALLOW_LINK
1153    if (x >= 0 && x < STBTE_MAX_TILEMAP_X && y >= 0 && y < STBTE_MAX_TILEMAP_Y) {
1154       gx = tm->link[y][x].x;
1155       gy = tm->link[y][x].y;
1156       if (gx >= 0)
1157          if (!STBTE_ALLOW_LINK(tm->data[y][x], tm->props[y][x], tm->data[gy][gx], tm->props[gy][gx]))
1158             gx = gy = -1;
1159    }
1160 #endif
1161    *destx = gx;
1162    *desty = gy;
1163 }
1164 
stbte_set_property(stbte_tilemap * tm,int x,int y,int n,float val)1165 void stbte_set_property(stbte_tilemap *tm, int x, int y, int n, float val)
1166 {
1167    tm->props[y][x][n] = val;
1168 }
1169 
1170 static void stbte__set_link(stbte_tilemap *tm, int src_x, int src_y, int dest_x, int dest_y, int undo_mode);
1171 
1172 enum
1173 {
1174    STBTE__undo_none,
1175    STBTE__undo_record,
1176    STBTE__undo_block,
1177 };
1178 
stbte_set_link(stbte_tilemap * tm,int x,int y,int destx,int desty)1179 void stbte_set_link(stbte_tilemap *tm, int x, int y, int destx, int desty)
1180 {
1181 #ifdef STBTE_ALLOW_LINK
1182    stbte__set_link(tm, x, y, destx, desty, STBTE__undo_none);
1183 #else
1184    STBTE_ASSERT(0);
1185 #endif
1186 }
1187 
1188 
1189 // returns an array of map_layers shorts. each short is either
1190 // one of the tile_id values from define_tile, or STBTE_EMPTY
1191 
stbte_set_dimensions(stbte_tilemap * tm,int map_x,int map_y)1192 void stbte_set_dimensions(stbte_tilemap *tm, int map_x, int map_y)
1193 {
1194    STBTE_ASSERT(map_x >= 0 && map_x <= STBTE_MAX_TILEMAP_X);
1195    STBTE_ASSERT(map_y >= 0 && map_y <= STBTE_MAX_TILEMAP_Y);
1196    if (map_x < 0 || map_y < 0 || map_x > STBTE_MAX_TILEMAP_X || map_y > STBTE_MAX_TILEMAP_Y)
1197       return;
1198    tm->max_x = map_x;
1199    tm->max_y = map_y;
1200 }
1201 
stbte_clear_map(stbte_tilemap * tm)1202 void stbte_clear_map(stbte_tilemap *tm)
1203 {
1204    int i,j;
1205    for (i=0; i < STBTE_MAX_TILEMAP_X * STBTE_MAX_TILEMAP_Y; ++i) {
1206       tm->data[0][i][0] = tm->background_tile;
1207       for (j=1; j < tm->num_layers; ++j)
1208          tm->data[0][i][j] = STBTE__NO_TILE;
1209       for (j=0; j < STBTE_MAX_PROPERTIES; ++j)
1210          tm->props[0][i][j] = 0;
1211       #ifdef STBTE_ALLOW_LINK
1212       tm->link[0][i].x = -1;
1213       tm->link[0][i].y = -1;
1214       tm->linkcount[0][i] = 0;
1215       #endif
1216    }
1217 }
1218 
stbte_set_tile(stbte_tilemap * tm,int x,int y,int layer,signed short tile)1219 void stbte_set_tile(stbte_tilemap *tm, int x, int y, int layer, signed short tile)
1220 {
1221    STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
1222    STBTE_ASSERT(layer >= 0 && layer < tm->num_layers);
1223    STBTE_ASSERT(tile >= -1 && tile < 32768);
1224    if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y)
1225       return;
1226    if (layer < 0 || layer >= tm->num_layers || tile < -1)
1227       return;
1228    tm->data[y][x][layer] = tile;
1229 }
1230 
stbte__choose_category(stbte_tilemap * tm,int category)1231 static void stbte__choose_category(stbte_tilemap *tm, int category)
1232 {
1233    int i,n=0;
1234    tm->cur_category = category;
1235    for (i=0; i < tm->num_tiles; ++i)
1236       if (tm->tiles[i].category_id == category || category == -1)
1237          ++n;
1238    tm->cur_palette_count = n;
1239    tm->palette_scroll = 0;
1240 }
1241 
stbte__strequal(char * p,char * q)1242 static int stbte__strequal(char *p, char *q)
1243 {
1244    while (*p)
1245       if (*p++ != *q++) return 0;
1246    return *q == 0;
1247 }
1248 
stbte__compute_tileinfo(stbte_tilemap * tm)1249 static void stbte__compute_tileinfo(stbte_tilemap *tm)
1250 {
1251    int i,j,n=0;
1252 
1253    tm->num_categories=0;
1254 
1255    for (i=0; i < tm->num_tiles; ++i) {
1256       stbte__tileinfo *t = &tm->tiles[i];
1257       // find category
1258       for (j=0; j < tm->num_categories; ++j)
1259          if (stbte__strequal(t->category, tm->categories[j]))
1260             goto found;
1261       tm->categories[j] = t->category;
1262       ++tm->num_categories;
1263      found:
1264       t->category_id = (unsigned short) j;
1265    }
1266 
1267    // currently number of categories can never decrease because you
1268    // can't remove tile definitions, but let's get it right anyway
1269    if (tm->cur_category > tm->num_categories) {
1270       tm->cur_category = -1;
1271    }
1272 
1273    stbte__choose_category(tm, tm->cur_category);
1274 
1275    tm->tileinfo_dirty = 0;
1276 }
1277 
stbte__prepare_tileinfo(stbte_tilemap * tm)1278 static void stbte__prepare_tileinfo(stbte_tilemap *tm)
1279 {
1280    if (tm->tileinfo_dirty)
1281       stbte__compute_tileinfo(tm);
1282 }
1283 
1284 
1285 /////////////////////// undo system ////////////////////////
1286 
1287 // the undo system works by storing "commands" into a buffer, and
1288 // then playing back those commands. undo and redo have to store
1289 // the commands in different order.
1290 //
1291 // the commands are:
1292 //
1293 // 1)  end_of_undo_record
1294 //       -1:short
1295 //
1296 // 2)  end_of_redo_record
1297 //       -2:short
1298 //
1299 // 3)  tile update
1300 //       tile_id:short (-1..32767)
1301 //       x_coord:short
1302 //       y_coord:short
1303 //       layer:short (0..31)
1304 //
1305 // 4)  property update (also used for links)
1306 //       value_hi:short
1307 //       value_lo:short
1308 //       y_coord:short
1309 //       x_coord:short
1310 //       property:short (256+prop#)
1311 //
1312 // Since we use a circular buffer, we might overwrite the undo storage.
1313 // To detect this, before playing back commands we scan back and see
1314 // if we see an end_of_undo_record before hitting the relevant boundary,
1315 // it's wholly contained.
1316 //
1317 // When we read back through, we see them in reverse order, so
1318 // we'll see the layer number or property number first
1319 //
1320 // To be clearer about the circular buffer, there are two cases:
1321 //     1. a single record is larger than the whole buffer.
1322 //        this is caught because the end_of_undo_record will
1323 //        get overwritten.
1324 //     2. multiple records written are larger than the whole
1325 //        buffer, so some of them have been overwritten by
1326 //        the later ones. this is handled by explicitly tracking
1327 //        the undo length; we never try to parse the data that
1328 //        got overwritten
1329 
1330 // given two points, compute the length between them
1331 #define stbte__wrap(pos)            ((pos) & (STBTE__UNDO_BUFFER_COUNT-1))
1332 
1333 #define STBTE__undo_record  -2
1334 #define STBTE__redo_record  -3
1335 #define STBTE__undo_junk    -4  // this is written underneath the undo pointer, never used
1336 
stbte__write_undo(stbte_tilemap * tm,short value)1337 static void stbte__write_undo(stbte_tilemap *tm, short value)
1338 {
1339    int pos = tm->undo_pos;
1340    tm->undo_buffer[pos] = value;
1341    tm->undo_pos = stbte__wrap(pos+1);
1342    tm->undo_len += (tm->undo_len < STBTE__UNDO_BUFFER_COUNT-2);
1343    tm->redo_len -= (tm->redo_len > 0);
1344    tm->undo_available_valid = 0;
1345 }
1346 
stbte__write_redo(stbte_tilemap * tm,short value)1347 static void stbte__write_redo(stbte_tilemap *tm, short value)
1348 {
1349    int pos = tm->undo_pos;
1350    tm->undo_buffer[pos] = value;
1351    tm->undo_pos = stbte__wrap(pos-1);
1352    tm->redo_len += (tm->redo_len < STBTE__UNDO_BUFFER_COUNT-2);
1353    tm->undo_len -= (tm->undo_len > 0);
1354    tm->undo_available_valid = 0;
1355 }
1356 
stbte__begin_undo(stbte_tilemap * tm)1357 static void stbte__begin_undo(stbte_tilemap *tm)
1358 {
1359    tm->redo_len = 0;
1360    stbte__write_undo(tm, STBTE__undo_record);
1361    stbte__ui.undoing = 1;
1362    stbte__ui.alert_msg = 0; // clear alert if they start doing something
1363 }
1364 
stbte__end_undo(stbte_tilemap * tm)1365 static void stbte__end_undo(stbte_tilemap *tm)
1366 {
1367    if (stbte__ui.undoing) {
1368       // check if anything got written
1369       int pos = stbte__wrap(tm->undo_pos-1);
1370       if (tm->undo_buffer[pos] == STBTE__undo_record) {
1371          // empty undo record, move back
1372          tm->undo_pos = pos;
1373          STBTE_ASSERT(tm->undo_len > 0);
1374          tm->undo_len -= 1;
1375       }
1376       tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk;
1377       // otherwise do nothing
1378 
1379       stbte__ui.undoing = 0;
1380    }
1381 }
1382 
stbte__undo_record(stbte_tilemap * tm,int x,int y,int i,int v)1383 static void stbte__undo_record(stbte_tilemap *tm, int x, int y, int i, int v)
1384 {
1385    STBTE_ASSERT(stbte__ui.undoing);
1386    if (stbte__ui.undoing) {
1387       stbte__write_undo(tm, v);
1388       stbte__write_undo(tm, x);
1389       stbte__write_undo(tm, y);
1390       stbte__write_undo(tm, i);
1391    }
1392 }
1393 
stbte__redo_record(stbte_tilemap * tm,int x,int y,int i,int v)1394 static void stbte__redo_record(stbte_tilemap *tm, int x, int y, int i, int v)
1395 {
1396    stbte__write_redo(tm, v);
1397    stbte__write_redo(tm, x);
1398    stbte__write_redo(tm, y);
1399    stbte__write_redo(tm, i);
1400 }
1401 
stbte__extract_float(short s0,short s1)1402 static float stbte__extract_float(short s0, short s1)
1403 {
1404    union { float f; short s[2]; } converter;
1405    converter.s[0] = s0;
1406    converter.s[1] = s1;
1407    return converter.f;
1408 }
1409 
stbte__extract_short(float f,int slot)1410 static short stbte__extract_short(float f, int slot)
1411 {
1412    union { float f; short s[2]; } converter;
1413    converter.f = f;
1414    return converter.s[slot];
1415 }
1416 
stbte__undo_record_prop(stbte_tilemap * tm,int x,int y,int i,short s0,short s1)1417 static void stbte__undo_record_prop(stbte_tilemap *tm, int x, int y, int i, short s0, short s1)
1418 {
1419    STBTE_ASSERT(stbte__ui.undoing);
1420    if (stbte__ui.undoing) {
1421       stbte__write_undo(tm, s1);
1422       stbte__write_undo(tm, s0);
1423       stbte__write_undo(tm, x);
1424       stbte__write_undo(tm, y);
1425       stbte__write_undo(tm, 256+i);
1426    }
1427 }
1428 
stbte__undo_record_prop_float(stbte_tilemap * tm,int x,int y,int i,float f)1429 static void stbte__undo_record_prop_float(stbte_tilemap *tm, int x, int y, int i, float f)
1430 {
1431    stbte__undo_record_prop(tm, x,y,i, stbte__extract_short(f,0), stbte__extract_short(f,1));
1432 }
1433 
stbte__redo_record_prop(stbte_tilemap * tm,int x,int y,int i,short s0,short s1)1434 static void stbte__redo_record_prop(stbte_tilemap *tm, int x, int y, int i, short s0, short s1)
1435 {
1436    stbte__write_redo(tm, s1);
1437    stbte__write_redo(tm, s0);
1438    stbte__write_redo(tm, x);
1439    stbte__write_redo(tm, y);
1440    stbte__write_redo(tm, 256+i);
1441 }
1442 
1443 
stbte__undo_find_end(stbte_tilemap * tm)1444 static int stbte__undo_find_end(stbte_tilemap *tm)
1445 {
1446    // first scan through for the end record
1447    int i, pos = stbte__wrap(tm->undo_pos-1);
1448    for (i=0; i < tm->undo_len;) {
1449       STBTE_ASSERT(tm->undo_buffer[pos] != STBTE__undo_junk);
1450       if (tm->undo_buffer[pos] == STBTE__undo_record)
1451          break;
1452       if (tm->undo_buffer[pos] >= 255)
1453          pos = stbte__wrap(pos-5), i += 5;
1454       else
1455          pos = stbte__wrap(pos-4), i += 4;
1456    }
1457    if (i >= tm->undo_len)
1458       return -1;
1459    return pos;
1460 }
1461 
stbte__undo(stbte_tilemap * tm)1462 static void stbte__undo(stbte_tilemap *tm)
1463 {
1464    int i, pos, endpos;
1465    endpos = stbte__undo_find_end(tm);
1466    if (endpos < 0)
1467       return;
1468 
1469    // we found a complete undo record
1470    pos = stbte__wrap(tm->undo_pos-1);
1471 
1472    // start a redo record
1473    stbte__write_redo(tm, STBTE__redo_record);
1474 
1475    // so now go back through undo and apply in reverse
1476    // order, and copy it to redo
1477    for (i=0; endpos != pos; i += 4) {
1478       int x,y,n,v;
1479       // get the undo entry
1480       n = tm->undo_buffer[pos];
1481       y = tm->undo_buffer[stbte__wrap(pos-1)];
1482       x = tm->undo_buffer[stbte__wrap(pos-2)];
1483       v = tm->undo_buffer[stbte__wrap(pos-3)];
1484       if (n >= 255) {
1485          short s0=0,s1=0;
1486          int v2 = tm->undo_buffer[stbte__wrap(pos-4)];
1487          pos = stbte__wrap(pos-5);
1488          if (n > 255) {
1489             float vf = stbte__extract_float(v, v2);
1490             s0 = stbte__extract_short(tm->props[y][x][n-256], 0);
1491             s1 = stbte__extract_short(tm->props[y][x][n-256], 1);
1492             tm->props[y][x][n-256] = vf;
1493          } else {
1494 #ifdef STBTE_ALLOW_LINK
1495             s0 = tm->link[y][x].x;
1496             s1 = tm->link[y][x].y;
1497             stbte__set_link(tm, x,y, v, v2, STBTE__undo_none);
1498 #endif
1499          }
1500          // write the redo entry
1501          stbte__redo_record_prop(tm, x, y, n-256, s0,s1);
1502          // apply the undo entry
1503       } else {
1504          pos = stbte__wrap(pos-4);
1505          // write the redo entry
1506          stbte__redo_record(tm, x, y, n, tm->data[y][x][n]);
1507          // apply the undo entry
1508          tm->data[y][x][n] = (short) v;
1509       }
1510    }
1511    // overwrite undo record with junk
1512    tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk;
1513 }
1514 
stbte__redo_find_end(stbte_tilemap * tm)1515 static int stbte__redo_find_end(stbte_tilemap *tm)
1516 {
1517    // first scan through for the end record
1518    int i, pos = stbte__wrap(tm->undo_pos+1);
1519    for (i=0; i < tm->redo_len;) {
1520       STBTE_ASSERT(tm->undo_buffer[pos] != STBTE__undo_junk);
1521       if (tm->undo_buffer[pos] == STBTE__redo_record)
1522          break;
1523       if (tm->undo_buffer[pos] >= 255)
1524          pos = stbte__wrap(pos+5), i += 5;
1525       else
1526          pos = stbte__wrap(pos+4), i += 4;
1527    }
1528    if (i >= tm->redo_len)
1529       return -1; // this should only ever happen if redo buffer is empty
1530    return pos;
1531 }
1532 
stbte__redo(stbte_tilemap * tm)1533 static void stbte__redo(stbte_tilemap *tm)
1534 {
1535    // first scan through for the end record
1536    int i, pos, endpos;
1537    endpos = stbte__redo_find_end(tm);
1538    if (endpos < 0)
1539       return;
1540 
1541    // we found a complete redo record
1542    pos = stbte__wrap(tm->undo_pos+1);
1543 
1544    // start an undo record
1545    stbte__write_undo(tm, STBTE__undo_record);
1546 
1547    for (i=0; pos != endpos; i += 4) {
1548       int x,y,n,v;
1549       n = tm->undo_buffer[pos];
1550       y = tm->undo_buffer[stbte__wrap(pos+1)];
1551       x = tm->undo_buffer[stbte__wrap(pos+2)];
1552       v = tm->undo_buffer[stbte__wrap(pos+3)];
1553       if (n >= 255) {
1554          int v2 = tm->undo_buffer[stbte__wrap(pos+4)];
1555          short s0=0,s1=0;
1556          pos = stbte__wrap(pos+5);
1557          if (n > 255) {
1558             float vf = stbte__extract_float(v, v2);
1559             s0 = stbte__extract_short(tm->props[y][x][n-256],0);
1560             s1 = stbte__extract_short(tm->props[y][x][n-256],1);
1561             tm->props[y][x][n-256] = vf;
1562          } else {
1563 #ifdef STBTE_ALLOW_LINK
1564             s0 = tm->link[y][x].x;
1565             s1 = tm->link[y][x].y;
1566             stbte__set_link(tm, x,y,v,v2, STBTE__undo_none);
1567 #endif
1568          }
1569          // don't use stbte__undo_record_prop because it's guarded
1570          stbte__write_undo(tm, s1);
1571          stbte__write_undo(tm, s0);
1572          stbte__write_undo(tm, x);
1573          stbte__write_undo(tm, y);
1574          stbte__write_undo(tm, n);
1575       } else {
1576          pos = stbte__wrap(pos+4);
1577          // don't use stbte__undo_record because it's guarded
1578          stbte__write_undo(tm, tm->data[y][x][n]);
1579          stbte__write_undo(tm, x);
1580          stbte__write_undo(tm, y);
1581          stbte__write_undo(tm, n);
1582          tm->data[y][x][n] = (short) v;
1583       }
1584    }
1585    tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk;
1586 }
1587 
1588 // because detecting that undo is available
stbte__recompute_undo_available(stbte_tilemap * tm)1589 static void stbte__recompute_undo_available(stbte_tilemap *tm)
1590 {
1591    tm->undo_available = (stbte__undo_find_end(tm) >= 0);
1592    tm->redo_available = (stbte__redo_find_end(tm) >= 0);
1593 }
1594 
stbte__undo_available(stbte_tilemap * tm)1595 static int stbte__undo_available(stbte_tilemap *tm)
1596 {
1597    if (!tm->undo_available_valid)
1598       stbte__recompute_undo_available(tm);
1599    return tm->undo_available;
1600 }
1601 
stbte__redo_available(stbte_tilemap * tm)1602 static int stbte__redo_available(stbte_tilemap *tm)
1603 {
1604    if (!tm->undo_available_valid)
1605       stbte__recompute_undo_available(tm);
1606    return tm->redo_available;
1607 }
1608 
1609 ///////////////////////////////////////////////////////////////////////////////////////////////////
1610 
1611 #ifdef STBTE_ALLOW_LINK
stbte__set_link(stbte_tilemap * tm,int src_x,int src_y,int dest_x,int dest_y,int undo_mode)1612 static void stbte__set_link(stbte_tilemap *tm, int src_x, int src_y, int dest_x, int dest_y, int undo_mode)
1613 {
1614    stbte__link *a;
1615    STBTE_ASSERT(src_x >= 0 && src_x < STBTE_MAX_TILEMAP_X && src_y >= 0 && src_y < STBTE_MAX_TILEMAP_Y);
1616    a = &tm->link[src_y][src_x];
1617    // check if it's a do nothing
1618    if (a->x == dest_x && a->y == dest_y)
1619       return;
1620    if (undo_mode != STBTE__undo_none ) {
1621       if (undo_mode == STBTE__undo_block) stbte__begin_undo(tm);
1622       stbte__undo_record_prop(tm, src_x, src_y, -1, a->x, a->y);
1623       if (undo_mode == STBTE__undo_block) stbte__end_undo(tm);
1624    }
1625    // check if there's an existing link
1626    if (a->x >= 0) {
1627       // decrement existing link refcount
1628       STBTE_ASSERT(tm->linkcount[a->y][a->x] > 0);
1629       --tm->linkcount[a->y][a->x];
1630    }
1631    // increment new dest
1632    if (dest_x >= 0) {
1633       ++tm->linkcount[dest_y][dest_x];
1634    }
1635    a->x = dest_x;
1636    a->y = dest_y;
1637 }
1638 #endif
1639 
1640 
stbte__draw_rect(int x0,int y0,int x1,int y1,unsigned int color)1641 static void stbte__draw_rect(int x0, int y0, int x1, int y1, unsigned int color)
1642 {
1643    STBTE_DRAW_RECT(x0,y0,x1,y1, color);
1644 }
1645 
stbte__draw_line(int x0,int y0,int x1,int y1,unsigned int color)1646 static void stbte__draw_line(int x0, int y0, int x1, int y1, unsigned int color)
1647 {
1648    int temp;
1649    if (x1 < x0) temp=x0,x0=x1,x1=temp;
1650    if (y1 < y0) temp=y0,y0=y1,y1=temp;
1651    stbte__draw_rect(x0,y0,x1+1,y1+1,color);
1652 }
1653 
stbte__draw_link(int x0,int y0,int x1,int y1,unsigned int color)1654 static void stbte__draw_link(int x0, int y0, int x1, int y1, unsigned int color)
1655 {
1656    stbte__draw_line(x0,y0,x0,y1, color);
1657    stbte__draw_line(x0,y1,x1,y1, color);
1658 }
1659 
stbte__draw_frame(int x0,int y0,int x1,int y1,unsigned int color)1660 static void stbte__draw_frame(int x0, int y0, int x1, int y1, unsigned int color)
1661 {
1662    stbte__draw_rect(x0,y0,x1-1,y0+1,color);
1663    stbte__draw_rect(x1-1,y0,x1,y1-1,color);
1664    stbte__draw_rect(x0+1,y1-1,x1,y1,color);
1665    stbte__draw_rect(x0,y0+1,x0+1,y1,color);
1666 }
1667 
stbte__draw_halfframe(int x0,int y0,int x1,int y1,unsigned int color)1668 static void stbte__draw_halfframe(int x0, int y0, int x1, int y1, unsigned int color)
1669 {
1670    stbte__draw_rect(x0,y0,x1,y0+1,color);
1671    stbte__draw_rect(x0,y0+1,x0+1,y1,color);
1672 }
1673 
stbte__get_char_width(int ch)1674 static int stbte__get_char_width(int ch)
1675 {
1676    return stbte__fontdata[ch-16];
1677 }
1678 
stbte__get_char_bitmap(int ch)1679 static short *stbte__get_char_bitmap(int ch)
1680 {
1681    return stbte__fontdata + stbte__font_offset[ch-16];
1682 }
1683 
stbte__draw_bitmask_as_columns(int x,int y,short bitmask,int color)1684 static void stbte__draw_bitmask_as_columns(int x, int y, short bitmask, int color)
1685 {
1686    int start_i = -1, i=0;
1687    while (bitmask) {
1688       if (bitmask & (1<<i)) {
1689          if (start_i < 0)
1690             start_i = i;
1691       } else if (start_i >= 0) {
1692          stbte__draw_rect(x, y+start_i, x+1, y+i, color);
1693          start_i = -1;
1694          bitmask &= ~((1<<i)-1); // clear all the old bits; we don't clear them as we go to save code
1695       }
1696       ++i;
1697    }
1698 }
1699 
stbte__draw_bitmap(int x,int y,int w,short * bitmap,int color)1700 static void stbte__draw_bitmap(int x, int y, int w, short *bitmap, int color)
1701 {
1702    int i;
1703    for (i=0; i < w; ++i)
1704       stbte__draw_bitmask_as_columns(x+i, y, *bitmap++, color);
1705 }
1706 
stbte__draw_text_core(int x,int y,const char * str,int w,int color,int digitspace)1707 static void stbte__draw_text_core(int x, int y, const char *str, int w, int color, int digitspace)
1708 {
1709    int x_end = x+w;
1710    while (*str) {
1711       int c = *str++;
1712       int cw = stbte__get_char_width(c);
1713       if (x + cw > x_end)
1714          break;
1715       stbte__draw_bitmap(x, y, cw, stbte__get_char_bitmap(c), color);
1716       if (digitspace && c == ' ')
1717          cw = stbte__get_char_width('0');
1718       x += cw+1;
1719    }
1720 }
1721 
stbte__draw_text(int x,int y,const char * str,int w,int color)1722 static void stbte__draw_text(int x, int y, const char *str, int w, int color)
1723 {
1724    stbte__draw_text_core(x,y,str,w,color,0);
1725 }
1726 
stbte__text_width(const char * str)1727 static int stbte__text_width(const char *str)
1728 {
1729    int x = 0;
1730    while (*str) {
1731       int c = *str++;
1732       int cw = stbte__get_char_width(c);
1733       x += cw+1;
1734    }
1735    return x;
1736 }
1737 
stbte__draw_frame_delayed(int x0,int y0,int x1,int y1,int color)1738 static void stbte__draw_frame_delayed(int x0, int y0, int x1, int y1, int color)
1739 {
1740    if (stbte__ui.delaycount < STBTE__MAX_DELAYRECT) {
1741       stbte__colorrect r = { x0,y0,x1,y1,color };
1742       stbte__ui.delayrect[stbte__ui.delaycount++] = r;
1743    }
1744 }
1745 
stbte__flush_delay(void)1746 static void stbte__flush_delay(void)
1747 {
1748    stbte__colorrect *r;
1749    int i;
1750    r = stbte__ui.delayrect;
1751    for (i=0; i < stbte__ui.delaycount; ++i,++r)
1752       stbte__draw_frame(r->x0,r->y0,r->x1,r->y1,r->color);
1753    stbte__ui.delaycount = 0;
1754 }
1755 
stbte__activate(int id)1756 static void stbte__activate(int id)
1757 {
1758    stbte__ui.active_id = id;
1759    stbte__ui.active_event = stbte__ui.event;
1760    stbte__ui.accum_x = 0;
1761    stbte__ui.accum_y = 0;
1762 }
1763 
stbte__hittest(int x0,int y0,int x1,int y1,int id)1764 static int stbte__hittest(int x0, int y0, int x1, int y1, int id)
1765 {
1766    int over =    stbte__ui.mx >= x0 && stbte__ui.my >= y0
1767               && stbte__ui.mx <  x1 && stbte__ui.my <  y1;
1768 
1769    if (over && stbte__ui.event >= STBTE__tick)
1770       stbte__ui.next_hot_id = id;
1771 
1772    return over;
1773 }
1774 
stbte__button_core(int id)1775 static int stbte__button_core(int id)
1776 {
1777    switch (stbte__ui.event) {
1778       case STBTE__leftdown:
1779          if (stbte__ui.hot_id == id && STBTE__INACTIVE())
1780             stbte__activate(id);
1781          break;
1782       case STBTE__leftup:
1783          if (stbte__ui.active_id == id && STBTE__IS_HOT(id)) {
1784             stbte__activate(0);
1785             return 1;
1786          }
1787          break;
1788       case STBTE__rightdown:
1789          if (stbte__ui.hot_id == id && STBTE__INACTIVE())
1790             stbte__activate(id);
1791          break;
1792       case STBTE__rightup:
1793          if (stbte__ui.active_id == id && STBTE__IS_HOT(id)) {
1794             stbte__activate(0);
1795             return -1;
1796          }
1797          break;
1798    }
1799    return 0;
1800 }
1801 
stbte__draw_box(int x0,int y0,int x1,int y1,int colormode,int colorindex)1802 static void stbte__draw_box(int x0, int y0, int x1, int y1, int colormode, int colorindex)
1803 {
1804    stbte__draw_rect (x0,y0,x1,y1, stbte__color_table[colormode][STBTE__base   ][colorindex]);
1805    stbte__draw_frame(x0,y0,x1,y1, stbte__color_table[colormode][STBTE__outline][colorindex]);
1806 }
1807 
stbte__draw_textbox(int x0,int y0,int x1,int y1,char * text,int xoff,int yoff,int colormode,int colorindex)1808 static void stbte__draw_textbox(int x0, int y0, int x1, int y1, char *text, int xoff, int yoff, int colormode, int colorindex)
1809 {
1810    stbte__draw_box(x0,y0,x1,y1,colormode,colorindex);
1811    stbte__draw_text(x0+xoff,y0+yoff, text, x1-x0-xoff-1, stbte__color_table[colormode][STBTE__text][colorindex]);
1812 }
1813 
stbte__button(int colormode,char * label,int x,int y,int textoff,int width,int id,int toggled,int disabled)1814 static int stbte__button(int colormode, char *label, int x, int y, int textoff, int width, int id, int toggled, int disabled)
1815 {
1816    int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT;
1817    int s = STBTE__BUTTON_INTERNAL_SPACING;
1818 
1819    int over = !disabled && stbte__hittest(x0,y0,x1,y1,id);
1820 
1821    if (stbte__ui.event == STBTE__paint)
1822       stbte__draw_textbox(x0,y0,x1,y1, label,s+textoff,s, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled));
1823    if (disabled)
1824       return 0;
1825    return (stbte__button_core(id) == 1);
1826 }
1827 
stbte__button_icon(int colormode,char ch,int x,int y,int width,int id,int toggled,int disabled)1828 static int stbte__button_icon(int colormode, char ch, int x, int y, int width, int id, int toggled, int disabled)
1829 {
1830    int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT;
1831    int s = STBTE__BUTTON_INTERNAL_SPACING;
1832 
1833    int over = stbte__hittest(x0,y0,x1,y1,id);
1834 
1835    if (stbte__ui.event == STBTE__paint) {
1836       char label[2] = { ch, 0 };
1837       int pad = (9 - stbte__get_char_width(ch))/2;
1838       stbte__draw_textbox(x0,y0,x1,y1, label,s+pad,s, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled));
1839    }
1840    if (disabled)
1841       return 0;
1842    return (stbte__button_core(id) == 1);
1843 }
1844 
stbte__minibutton(int colormode,int x,int y,int ch,int id)1845 static int stbte__minibutton(int colormode, int x, int y, int ch, int id)
1846 {
1847    int x0 = x, y0 = y, x1 = x+8, y1 = y+7;
1848    int over = stbte__hittest(x0,y0,x1,y1,id);
1849    if (stbte__ui.event == STBTE__paint) {
1850       char str[2] = { ch,0 };
1851       stbte__draw_textbox(x0,y0,x1,y1, str,1,0,colormode, STBTE__INDEX_FOR_ID(id,0,0));
1852    }
1853    return stbte__button_core(id);
1854 }
1855 
stbte__layerbutton(int x,int y,int ch,int id,int toggled,int disabled,int colormode)1856 static int stbte__layerbutton(int x, int y, int ch, int id, int toggled, int disabled, int colormode)
1857 {
1858    int x0 = x, y0 = y, x1 = x+10, y1 = y+11;
1859    int over = !disabled && stbte__hittest(x0,y0,x1,y1,id);
1860    if (stbte__ui.event == STBTE__paint) {
1861       char str[2] = { ch,0 };
1862       int off = (9-stbte__get_char_width(ch))/2;
1863       stbte__draw_textbox(x0,y0,x1,y1, str, off+1,2, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled));
1864    }
1865    if (disabled)
1866       return 0;
1867    return stbte__button_core(id);
1868 }
1869 
stbte__microbutton(int x,int y,int size,int id,int colormode)1870 static int stbte__microbutton(int x, int y, int size, int id, int colormode)
1871 {
1872    int x0 = x, y0 = y, x1 = x+size, y1 = y+size;
1873    int over = stbte__hittest(x0,y0,x1,y1,id);
1874    if (stbte__ui.event == STBTE__paint) {
1875       stbte__draw_box(x0,y0,x1,y1, colormode, STBTE__INDEX_FOR_ID(id,0,0));
1876    }
1877    return stbte__button_core(id);
1878 }
1879 
stbte__microbutton_dragger(int x,int y,int size,int id,int * pos)1880 static int stbte__microbutton_dragger(int x, int y, int size, int id, int *pos)
1881 {
1882    int x0 = x, y0 = y, x1 = x+size, y1 = y+size;
1883    int over = stbte__hittest(x0,y0,x1,y1,id);
1884    switch (stbte__ui.event) {
1885       case STBTE__paint:
1886          stbte__draw_box(x0,y0,x1,y1, STBTE__cexpander, STBTE__INDEX_FOR_ID(id,0,0));
1887          break;
1888       case STBTE__leftdown:
1889          if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
1890             stbte__activate(id);
1891             stbte__ui.sx = stbte__ui.mx - *pos;
1892          }
1893          break;
1894       case STBTE__mousemove:
1895          if (STBTE__IS_ACTIVE(id) && stbte__ui.active_event == STBTE__leftdown) {
1896             *pos = stbte__ui.mx - stbte__ui.sx;
1897          }
1898          break;
1899       case STBTE__leftup:
1900          if (STBTE__IS_ACTIVE(id))
1901             stbte__activate(0);
1902          break;
1903       default:
1904          return stbte__button_core(id);
1905    }
1906    return 0;
1907 }
1908 
stbte__category_button(char * label,int x,int y,int width,int id,int toggled)1909 static int stbte__category_button(char *label, int x, int y, int width, int id, int toggled)
1910 {
1911    int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT;
1912    int s = STBTE__BUTTON_INTERNAL_SPACING;
1913 
1914    int over = stbte__hittest(x0,y0,x1,y1,id);
1915 
1916    if (stbte__ui.event == STBTE__paint)
1917       stbte__draw_textbox(x0,y0,x1,y1, label, s,s, STBTE__ccategory_button, STBTE__INDEX_FOR_ID(id,0,toggled));
1918 
1919    return (stbte__button_core(id) == 1);
1920 }
1921 
1922 enum
1923 {
1924    STBTE__none,
1925    STBTE__begin,
1926    STBTE__end,
1927    STBTE__change,
1928 };
1929 
1930 // returns -1 if value changes, 1 at end of drag
stbte__slider(int x0,int w,int y,int range,int * value,int id)1931 static int stbte__slider(int x0, int w, int y, int range, int *value, int id)
1932 {
1933    int x1 = x0+w;
1934    int pos = *value * w / (range+1);
1935    int over = stbte__hittest(x0,y-2,x1,y+3,id);
1936    int event_mouse_move = STBTE__change;
1937    switch (stbte__ui.event) {
1938       case STBTE__paint:
1939          stbte__draw_rect(x0,y,x1,y+1, 0x808080);
1940          stbte__draw_rect(x0+pos-1,y-1,x0+pos+2,y+2, 0xffffff);
1941          break;
1942       case STBTE__leftdown:
1943          if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
1944             stbte__activate(id);
1945             event_mouse_move = STBTE__begin;
1946          }
1947          // fall through
1948       case STBTE__mousemove:
1949          if (STBTE__IS_ACTIVE(id)) {
1950             int v = (stbte__ui.mx-x0)*(range+1)/w;
1951             if (v < 0) v = 0; else if (v > range) v = range;
1952             *value = v;
1953             return event_mouse_move;
1954          }
1955          break;
1956       case STBTE__leftup:
1957          if (STBTE__IS_ACTIVE(id)) {
1958             stbte__activate(0);
1959             return STBTE__end;
1960          }
1961          break;
1962    }
1963    return STBTE__none;
1964 }
1965 
stbte__float_control(int x0,int y0,int w,float minv,float maxv,float scale,char * fmt,float * value,int colormode,int id)1966 static int stbte__float_control(int x0, int y0, int w, float minv, float maxv, float scale, char *fmt, float *value, int colormode, int id)
1967 {
1968    int x1 = x0+w;
1969    int y1 = y0+11;
1970    int over = stbte__hittest(x0,y0,x1,y1,id);
1971    switch (stbte__ui.event) {
1972       case STBTE__paint: {
1973          char text[32];
1974          sprintf(text, fmt ? fmt : "%6.2f", *value);
1975          stbte__draw_textbox(x0,y0,x1,y1, text, 1,2, colormode, STBTE__INDEX_FOR_ID(id,0,0));
1976          break;
1977       }
1978       case STBTE__leftdown:
1979       case STBTE__rightdown:
1980          if (STBTE__IS_HOT(id) && STBTE__INACTIVE())
1981             stbte__activate(id);
1982             return STBTE__begin;
1983          break;
1984       case STBTE__leftup:
1985       case STBTE__rightup:
1986          if (STBTE__IS_ACTIVE(id)) {
1987             stbte__activate(0);
1988             return STBTE__end;
1989          }
1990          break;
1991       case STBTE__mousemove:
1992          if (STBTE__IS_ACTIVE(id)) {
1993             float v = *value, delta;
1994             int ax = stbte__ui.accum_x/STBTE_FLOAT_CONTROL_GRANULARITY;
1995             int ay = stbte__ui.accum_y/STBTE_FLOAT_CONTROL_GRANULARITY;
1996             stbte__ui.accum_x -= ax*STBTE_FLOAT_CONTROL_GRANULARITY;
1997             stbte__ui.accum_y -= ay*STBTE_FLOAT_CONTROL_GRANULARITY;
1998             if (stbte__ui.shift) {
1999                if (stbte__ui.active_event == STBTE__leftdown)
2000                   delta = ax * 16.0f + ay;
2001                else
2002                   delta = ax / 16.0f + ay / 256.0f;
2003             } else {
2004                if (stbte__ui.active_event == STBTE__leftdown)
2005                   delta = ax*10.0f + ay;
2006                else
2007                   delta = ax * 0.1f + ay * 0.01f;
2008             }
2009             v += delta * scale;
2010             if (v < minv) v = minv;
2011             if (v > maxv) v = maxv;
2012             *value = v;
2013             return STBTE__change;
2014          }
2015          break;
2016    }
2017    return STBTE__none;
2018 }
2019 
stbte__scrollbar(int x,int y0,int y1,int * val,int v0,int v1,int num_vis,int id)2020 static void stbte__scrollbar(int x, int y0, int y1, int *val, int v0, int v1, int num_vis, int id)
2021 {
2022    int over;
2023    int thumbpos;
2024    if (v1 - v0 <= num_vis)
2025       return;
2026 
2027    // generate thumbpos from numvis
2028    thumbpos = y0+2 + (y1-y0-4) * *val / (v1 - v0 - num_vis);
2029    if (thumbpos < y0) thumbpos = y0;
2030    if (thumbpos >= y1) thumbpos = y1;
2031    over = stbte__hittest(x-1,y0,x+2,y1,id);
2032    switch (stbte__ui.event) {
2033       case STBTE__paint:
2034          stbte__draw_rect(x,y0,x+1,y1, stbte__color_table[STBTE__cscrollbar][STBTE__text][STBTE__idle]);
2035          stbte__draw_box(x-1,thumbpos-3,x+2,thumbpos+4, STBTE__cscrollbar, STBTE__INDEX_FOR_ID(id,0,0));
2036          break;
2037       case STBTE__leftdown:
2038          if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
2039             // check if it's over the thumb
2040             stbte__activate(id);
2041             *val = ((stbte__ui.my-y0) * (v1 - v0 - num_vis) + (y1-y0)/2)/ (y1-y0);
2042          }
2043          break;
2044       case STBTE__mousemove:
2045          if (STBTE__IS_ACTIVE(id) && stbte__ui.mx >= x-15 && stbte__ui.mx <= x+15)
2046             *val = ((stbte__ui.my-y0) * (v1 - v0 - num_vis) + (y1-y0)/2)/ (y1-y0);
2047          break;
2048       case STBTE__leftup:
2049          if (STBTE__IS_ACTIVE(id))
2050             stbte__activate(0);
2051          break;
2052 
2053    }
2054 
2055    if (*val >= v1-num_vis)
2056       *val = v1-num_vis;
2057    if (*val <= v0)
2058       *val = v0;
2059 }
2060 
2061 
stbte__compute_digits(stbte_tilemap * tm)2062 static void stbte__compute_digits(stbte_tilemap *tm)
2063 {
2064    if (tm->max_x >= 1000 || tm->max_y >= 1000)
2065       tm->digits = 4;
2066    else if (tm->max_x >= 100 || tm->max_y >= 100)
2067       tm->digits = 3;
2068    else
2069       tm->digits = 2;
2070 }
2071 
stbte__is_single_selection(void)2072 static int stbte__is_single_selection(void)
2073 {
2074    return stbte__ui.has_selection
2075        && stbte__ui.select_x0 == stbte__ui.select_x1
2076        && stbte__ui.select_y0 == stbte__ui.select_y1;
2077 }
2078 
2079 typedef struct
2080 {
2081    int width, height;
2082    int x,y;
2083    int active;
2084    float retracted;
2085 } stbte__region_t;
2086 
2087 static stbte__region_t stbte__region[4];
2088 
2089 #define STBTE__TOOLBAR_ICON_SIZE   (9+2*2)
2090 #define STBTE__TOOLBAR_PASTE_SIZE  (34+2*2)
2091 
2092 // This routine computes where every panel goes onscreen: computes
2093 // a minimum width for each side based on which panels are on that
2094 // side, and accounts for width-dependent layout of certain panels.
stbte__compute_panel_locations(stbte_tilemap * tm)2095 static void stbte__compute_panel_locations(stbte_tilemap *tm)
2096 {
2097    int i, limit, w, k;
2098    int window_width  = stbte__ui.x1 - stbte__ui.x0;
2099    int window_height = stbte__ui.y1 - stbte__ui.y0;
2100    int min_width[STBTE__num_panel]={0,0,0,0,0,0,0};
2101    int height[STBTE__num_panel]={0,0,0,0,0,0,0};
2102    int panel_active[STBTE__num_panel]={1,0,1,1,1,1,1};
2103    int vpos[4] = { 0,0,0,0 };
2104    stbte__panel *p = stbte__ui.panel;
2105    stbte__panel *pt = &p[STBTE__panel_toolbar];
2106 #ifdef STBTE__NO_PROPS
2107    int props = 0;
2108 #else
2109    int props = 1;
2110 #endif
2111 
2112    for (i=0; i < 4; ++i) {
2113       stbte__region[i].active = 0;
2114       stbte__region[i].width = 0;
2115       stbte__region[i].height = 0;
2116    }
2117 
2118    // compute number of digits needs for info panel
2119    stbte__compute_digits(tm);
2120 
2121    // determine which panels are active
2122    panel_active[STBTE__panel_categories] = tm->num_categories != 0;
2123    panel_active[STBTE__panel_layers    ] = tm->num_layers     >  1;
2124 #ifdef STBTE__COLORPICKER
2125    panel_active[STBTE__panel_colorpick ] = 1;
2126 #endif
2127 
2128    panel_active[STBTE__panel_props     ] = props && stbte__is_single_selection();
2129 
2130    // compute minimum widths for each panel (assuming they're on sides not top)
2131    min_width[STBTE__panel_info      ] = 8 + 11 + 7*tm->digits+17+7;               // estimate min width of "w:0000"
2132    min_width[STBTE__panel_colorpick ] = 120;
2133    min_width[STBTE__panel_tiles     ] = 4 + tm->palette_spacing_x + 5;            // 5 for scrollbar
2134    min_width[STBTE__panel_categories] = 4 + 42 + 5;                               // 42 is enough to show ~7 chars; 5 for scrollbar
2135    min_width[STBTE__panel_layers    ] = 4 + 54 + 30*tm->has_layer_names;          // 2 digits plus 3 buttons plus scrollbar
2136    min_width[STBTE__panel_toolbar   ] = 4 + STBTE__TOOLBAR_PASTE_SIZE;            // wide enough for 'Paste' button
2137    min_width[STBTE__panel_props     ] = 80;                    // narrowest info panel
2138 
2139    // compute minimum widths for left & right panels based on the above
2140    stbte__region[0].width = stbte__ui.left_width;
2141    stbte__region[1].width = stbte__ui.right_width;
2142 
2143    for (i=0; i < STBTE__num_panel; ++i) {
2144       if (panel_active[i]) {
2145          int side = stbte__ui.panel[i].side;
2146          if (min_width[i] > stbte__region[side].width)
2147             stbte__region[side].width = min_width[i];
2148          stbte__region[side].active = 1;
2149       }
2150    }
2151 
2152    // now compute the heights of each panel
2153 
2154    // if toolbar at top, compute its size & push the left and right start points down
2155    if (stbte__region[STBTE__side_top].active) {
2156       int height = STBTE__TOOLBAR_ICON_SIZE+2;
2157       pt->x0     = stbte__ui.x0;
2158       pt->y0     = stbte__ui.y0;
2159       pt->width  = window_width;
2160       pt->height = height;
2161       vpos[STBTE__side_left] = vpos[STBTE__side_right] = height;
2162    } else {
2163       int num_rows = STBTE__num_tool * ((stbte__region[pt->side].width-4)/STBTE__TOOLBAR_ICON_SIZE);
2164       height[STBTE__panel_toolbar] = num_rows*13 + 3*15 + 4; // 3*15 for cut/copy/paste, which are stacked vertically
2165    }
2166 
2167    for (i=0; i < 4; ++i)
2168       stbte__region[i].y = stbte__ui.y0 + vpos[i];
2169 
2170    for (i=0; i < 2; ++i) {
2171       int anim = (int) (stbte__region[i].width * stbte__region[i].retracted);
2172       stbte__region[i].x = (i == STBTE__side_left) ? stbte__ui.x0 - anim : stbte__ui.x1 - stbte__region[i].width + anim;
2173    }
2174 
2175    // color picker
2176    height[STBTE__panel_colorpick] = 300;
2177 
2178    // info panel
2179    w = stbte__region[p[STBTE__panel_info].side].width;
2180    p[STBTE__panel_info].mode = (w >= 8 + (11+7*tm->digits+17)*2 + 4);
2181    if (p[STBTE__panel_info].mode)
2182       height[STBTE__panel_info] = 5 + 11*2 + 2 + tm->palette_spacing_y;
2183    else
2184       height[STBTE__panel_info] = 5 + 11*4 + 2 + tm->palette_spacing_y;
2185 
2186    // layers
2187    limit = 6 + stbte__ui.panel[STBTE__panel_layers].delta_height;
2188    height[STBTE__panel_layers] = (tm->num_layers > limit ? limit : tm->num_layers)*15 + 7 + (tm->has_layer_names ? 0 : 11) + props*13;
2189 
2190    // categories
2191    limit = 6 + stbte__ui.panel[STBTE__panel_categories].delta_height;
2192    height[STBTE__panel_categories] = (tm->num_categories+1 > limit ? limit : tm->num_categories+1)*11 + 14;
2193    if (stbte__ui.panel[STBTE__panel_categories].side == stbte__ui.panel[STBTE__panel_categories].side)
2194       height[STBTE__panel_categories] -= 4;
2195 
2196    // palette
2197    k =  (stbte__region[p[STBTE__panel_tiles].side].width - 8) / tm->palette_spacing_x;
2198    if (k == 0) k = 1;
2199    height[STBTE__panel_tiles] = ((tm->num_tiles+k-1)/k) * tm->palette_spacing_y + 8;
2200 
2201    // properties panel
2202    height[STBTE__panel_props] = 9 + STBTE_MAX_PROPERTIES*14;
2203 
2204    // now compute the locations of all the panels
2205    for (i=0; i < STBTE__num_panel; ++i) {
2206       if (panel_active[i]) {
2207          int side = p[i].side;
2208          if (side == STBTE__side_left || side == STBTE__side_right) {
2209             p[i].width  = stbte__region[side].width;
2210             p[i].x0     = stbte__region[side].x;
2211             p[i].y0     = stbte__ui.y0 + vpos[side];
2212             p[i].height = height[i];
2213             vpos[side] += height[i];
2214             if (vpos[side] > window_height) {
2215                vpos[side] = window_height;
2216                p[i].height = stbte__ui.y1 - p[i].y0;
2217             }
2218          } else {
2219             ; // it's at top, it's already been explicitly set up earlier
2220          }
2221       } else {
2222          // inactive panel
2223          p[i].height = 0;
2224          p[i].width  = 0;
2225          p[i].x0     = stbte__ui.x1;
2226          p[i].y0     = stbte__ui.y1;
2227       }
2228    }
2229 }
2230 
2231 // unique identifiers for imgui
2232 enum
2233 {
2234    STBTE__map=1,
2235    STBTE__region,
2236    STBTE__panel,                          // panel background to hide map, and misc controls
2237    STBTE__info,                           // info data
2238    STBTE__toolbarA, STBTE__toolbarB,      // toolbar buttons: param is tool number
2239    STBTE__palette,                        // palette selectors: param is tile index
2240    STBTE__categories,                     // category selectors: param is category index
2241    STBTE__layer,                          //
2242    STBTE__solo, STBTE__hide, STBTE__lock, // layer controls: param is layer
2243    STBTE__scrollbar,                      // param is panel ID
2244    STBTE__panel_mover,                    // p1 is panel ID, p2 is destination side
2245    STBTE__panel_sizer,                    // param panel ID
2246    STBTE__scrollbar_id,
2247    STBTE__colorpick_id,
2248    STBTE__prop_flag,
2249    STBTE__prop_float,
2250    STBTE__prop_int,
2251 };
2252 
2253 // id is:      [      24-bit data     : 7-bit identifer ]
2254 // map id is:  [  12-bit y : 12 bit x : 7-bit identifier ]
2255 
2256 #define STBTE__ID(n,p)     ((n) + ((p)<<7))
2257 #define STBTE__ID2(n,p,q)  STBTE__ID(n, ((p)<<12)+(q) )
2258 #define STBTE__IDMAP(x,y)  STBTE__ID2(STBTE__map, x,y)
2259 
stbte__activate_map(int x,int y)2260 static void stbte__activate_map(int x, int y)
2261 {
2262    stbte__ui.active_id = STBTE__IDMAP(x,y);
2263    stbte__ui.active_event = stbte__ui.event;
2264    stbte__ui.sx = x;
2265    stbte__ui.sy = y;
2266 }
2267 
stbte__alert(const char * msg)2268 static void stbte__alert(const char *msg)
2269 {
2270    stbte__ui.alert_msg = msg;
2271    stbte__ui.alert_timer = 3;
2272 }
2273 
2274 #define STBTE__BG(tm,layer) ((layer) == 0 ? (tm)->background_tile : STBTE__NO_TILE)
2275 
2276 
2277 
stbte__brush_predict(stbte_tilemap * tm,short result[])2278 static void stbte__brush_predict(stbte_tilemap *tm, short result[])
2279 {
2280    int layer_to_paint = tm->cur_layer;
2281    stbte__tileinfo *ti;
2282    int i;
2283 
2284    if (tm->cur_tile < 0) return;
2285 
2286    ti = &tm->tiles[tm->cur_tile];
2287 
2288    // find lowest legit layer to paint it on, and put it there
2289    for (i=0; i < tm->num_layers; ++i) {
2290       // check if object is allowed on layer
2291       if (!(ti->layermask & (1 << i)))
2292          continue;
2293 
2294       if (i != tm->solo_layer) {
2295          // if there's a selected layer, can only paint on that
2296          if (tm->cur_layer >= 0 && i != tm->cur_layer)
2297             continue;
2298 
2299          // if the layer is hidden, we can't see it
2300          if (tm->layerinfo[i].hidden)
2301             continue;
2302 
2303          // if the layer is locked, we can't write to it
2304          if (tm->layerinfo[i].locked == STBTE__locked)
2305             continue;
2306 
2307          // if the layer is non-empty and protected, can't write to it
2308          if (tm->layerinfo[i].locked == STBTE__protected && result[i] != STBTE__BG(tm,i))
2309             continue;
2310       }
2311 
2312       result[i] = ti->id;
2313       return;
2314    }
2315 }
2316 
stbte__brush(stbte_tilemap * tm,int x,int y)2317 static void stbte__brush(stbte_tilemap *tm, int x, int y)
2318 {
2319    int layer_to_paint = tm->cur_layer;
2320    stbte__tileinfo *ti;
2321 
2322    // find lowest legit layer to paint it on, and put it there
2323    int i;
2324 
2325    if (tm->cur_tile < 0) return;
2326 
2327    ti = &tm->tiles[tm->cur_tile];
2328 
2329    for (i=0; i < tm->num_layers; ++i) {
2330       // check if object is allowed on layer
2331       if (!(ti->layermask & (1 << i)))
2332          continue;
2333 
2334       if (i != tm->solo_layer) {
2335          // if there's a selected layer, can only paint on that
2336          if (tm->cur_layer >= 0 && i != tm->cur_layer)
2337             continue;
2338 
2339          // if the layer is hidden, we can't see it
2340          if (tm->layerinfo[i].hidden)
2341             continue;
2342 
2343          // if the layer is locked, we can't write to it
2344          if (tm->layerinfo[i].locked == STBTE__locked)
2345             continue;
2346 
2347          // if the layer is non-empty and protected, can't write to it
2348          if (tm->layerinfo[i].locked == STBTE__protected && tm->data[y][x][i] != STBTE__BG(tm,i))
2349             continue;
2350       }
2351 
2352       stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
2353       tm->data[y][x][i] = ti->id;
2354       return;
2355    }
2356 
2357    //stbte__alert("Selected tile not valid on active layer(s)");
2358 }
2359 
2360 enum
2361 {
2362    STBTE__erase_none = -1,
2363    STBTE__erase_brushonly = 0,
2364    STBTE__erase_any = 1,
2365    STBTE__erase_all = 2,
2366 };
2367 
stbte__erase_predict(stbte_tilemap * tm,short result[],int allow_any)2368 static int stbte__erase_predict(stbte_tilemap *tm, short result[], int allow_any)
2369 {
2370    stbte__tileinfo *ti = tm->cur_tile >= 0 ? &tm->tiles[tm->cur_tile] : NULL;
2371    int i;
2372 
2373    if (allow_any == STBTE__erase_none)
2374       return allow_any;
2375 
2376    // first check if only one layer is legit
2377    i = tm->cur_layer;
2378    if (tm->solo_layer >= 0)
2379       i = tm->solo_layer;
2380 
2381    // if only one layer is legit, directly process that one for clarity
2382    if (i >= 0) {
2383       short bg = (i == 0 ? tm->background_tile : -1);
2384       if (tm->solo_layer < 0) {
2385          // check that we're allowed to write to it
2386          if (tm->layerinfo[i].hidden) return STBTE__erase_none;
2387          if (tm->layerinfo[i].locked) return STBTE__erase_none;
2388       }
2389       if (result[i] == bg)
2390          return STBTE__erase_none; // didn't erase anything
2391       if (ti && result[i] == ti->id && (i != 0 || ti->id != tm->background_tile)) {
2392          result[i] = bg;
2393          return STBTE__erase_brushonly;
2394       }
2395       if (allow_any == STBTE__erase_any) {
2396          result[i] = bg;
2397          return STBTE__erase_any;
2398       }
2399       return STBTE__erase_none;
2400    }
2401 
2402    // if multiple layers are legit, first scan all for brush data
2403 
2404    if (ti && allow_any != STBTE__erase_all) {
2405       for (i=tm->num_layers-1; i >= 0; --i) {
2406          if (result[i] != ti->id)
2407             continue;
2408          if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden)
2409             continue;
2410          if (i == 0 && result[i] == tm->background_tile)
2411             return STBTE__erase_none;
2412          result[i] = STBTE__BG(tm,i);
2413          return STBTE__erase_brushonly;
2414       }
2415    }
2416 
2417    if (allow_any != STBTE__erase_any && allow_any != STBTE__erase_all)
2418       return STBTE__erase_none;
2419 
2420    // apply layer filters, erase from top
2421    for (i=tm->num_layers-1; i >= 0; --i) {
2422       if (result[i] < 0)
2423          continue;
2424       if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden)
2425          continue;
2426       if (i == 0 && result[i] == tm->background_tile)
2427          return STBTE__erase_none;
2428       result[i] = STBTE__BG(tm,i);
2429       if (allow_any != STBTE__erase_all)
2430          return STBTE__erase_any;
2431    }
2432 
2433    if (allow_any == STBTE__erase_all)
2434       return allow_any;
2435    return STBTE__erase_none;
2436 }
2437 
stbte__erase(stbte_tilemap * tm,int x,int y,int allow_any)2438 static int stbte__erase(stbte_tilemap *tm, int x, int y, int allow_any)
2439 {
2440    stbte__tileinfo *ti = tm->cur_tile >= 0 ? &tm->tiles[tm->cur_tile] : NULL;
2441    int i;
2442 
2443    if (allow_any == STBTE__erase_none)
2444       return allow_any;
2445 
2446    // first check if only one layer is legit
2447    i = tm->cur_layer;
2448    if (tm->solo_layer >= 0)
2449       i = tm->solo_layer;
2450 
2451    // if only one layer is legit, directly process that one for clarity
2452    if (i >= 0) {
2453       short bg = (i == 0 ? tm->background_tile : -1);
2454       if (tm->solo_layer < 0) {
2455          // check that we're allowed to write to it
2456          if (tm->layerinfo[i].hidden) return STBTE__erase_none;
2457          if (tm->layerinfo[i].locked) return STBTE__erase_none;
2458       }
2459       if (tm->data[y][x][i] == bg)
2460          return -1; // didn't erase anything
2461       if (ti && tm->data[y][x][i] == ti->id && (i != 0 || ti->id != tm->background_tile)) {
2462          stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
2463          tm->data[y][x][i] = bg;
2464          return STBTE__erase_brushonly;
2465       }
2466       if (allow_any == STBTE__erase_any) {
2467          stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
2468          tm->data[y][x][i] = bg;
2469          return STBTE__erase_any;
2470       }
2471       return STBTE__erase_none;
2472    }
2473 
2474    // if multiple layers are legit, first scan all for brush data
2475 
2476    if (ti && allow_any != STBTE__erase_all) {
2477       for (i=tm->num_layers-1; i >= 0; --i) {
2478          if (tm->data[y][x][i] != ti->id)
2479             continue;
2480          if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden)
2481             continue;
2482          if (i == 0 && tm->data[y][x][i] == tm->background_tile)
2483             return STBTE__erase_none;
2484          stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
2485          tm->data[y][x][i] = STBTE__BG(tm,i);
2486          return STBTE__erase_brushonly;
2487       }
2488    }
2489 
2490    if (allow_any != STBTE__erase_any && allow_any != STBTE__erase_all)
2491       return STBTE__erase_none;
2492 
2493    // apply layer filters, erase from top
2494    for (i=tm->num_layers-1; i >= 0; --i) {
2495       if (tm->data[y][x][i] < 0)
2496          continue;
2497       if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden)
2498          continue;
2499       if (i == 0 && tm->data[y][x][i] == tm->background_tile)
2500          return STBTE__erase_none;
2501       stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
2502       tm->data[y][x][i] = STBTE__BG(tm,i);
2503       if (allow_any != STBTE__erase_all)
2504          return STBTE__erase_any;
2505    }
2506    if (allow_any == STBTE__erase_all)
2507       return allow_any;
2508    return STBTE__erase_none;
2509 }
2510 
stbte__find_tile(stbte_tilemap * tm,int tile_id)2511 static int stbte__find_tile(stbte_tilemap *tm, int tile_id)
2512 {
2513    int i;
2514    for (i=0; i < tm->num_tiles; ++i)
2515       if (tm->tiles[i].id == tile_id)
2516          return i;
2517    stbte__alert("Eyedropped tile that isn't in tileset");
2518    return -1;
2519 }
2520 
stbte__eyedrop(stbte_tilemap * tm,int x,int y)2521 static void stbte__eyedrop(stbte_tilemap *tm, int x, int y)
2522 {
2523    int i,j;
2524 
2525    // flush eyedropper state
2526    if (stbte__ui.eyedrop_x != x || stbte__ui.eyedrop_y != y) {
2527       stbte__ui.eyedrop_x = x;
2528       stbte__ui.eyedrop_y = y;
2529       stbte__ui.eyedrop_last_layer = tm->num_layers;
2530    }
2531 
2532    // if only one layer is active, query that
2533    i = tm->cur_layer;
2534    if (tm->solo_layer >= 0)
2535       i = tm->solo_layer;
2536    if (i >= 0) {
2537       if (tm->data[y][x][i] == STBTE__NO_TILE)
2538          return;
2539       tm->cur_tile = stbte__find_tile(tm, tm->data[y][x][i]);
2540       return;
2541    }
2542 
2543    // if multiple layers, continue from previous
2544    i = stbte__ui.eyedrop_last_layer;
2545    for (j=0; j < tm->num_layers; ++j) {
2546       if (--i < 0)
2547          i = tm->num_layers-1;
2548       if (tm->layerinfo[i].hidden)
2549          continue;
2550       if (tm->data[y][x][i] == STBTE__NO_TILE)
2551          continue;
2552       stbte__ui.eyedrop_last_layer = i;
2553       tm->cur_tile = stbte__find_tile(tm, tm->data[y][x][i]);
2554       return;
2555    }
2556 }
2557 
stbte__should_copy_properties(stbte_tilemap * tm)2558 static int stbte__should_copy_properties(stbte_tilemap *tm)
2559 {
2560    int i;
2561    if (tm->propmode == STBTE__propmode_always)
2562       return 1;
2563    if (tm->propmode == STBTE__propmode_never)
2564       return 0;
2565    if (tm->solo_layer >= 0 || tm->cur_layer >= 0)
2566       return 0;
2567    for (i=0; i < tm->num_layers; ++i)
2568       if (tm->layerinfo[i].hidden || tm->layerinfo[i].locked)
2569          return 0;
2570    return 1;
2571 }
2572 
2573 // compute the result of pasting into a tile non-destructively so we can preview it
stbte__paste_stack(stbte_tilemap * tm,short result[],short dest[],short src[],int dragging)2574 static void stbte__paste_stack(stbte_tilemap *tm, short result[], short dest[], short src[], int dragging)
2575 {
2576    int i;
2577 
2578    // special case single-layer
2579    i = tm->cur_layer;
2580    if (tm->solo_layer >= 0)
2581       i = tm->solo_layer;
2582    if (i >= 0) {
2583       if (tm->solo_layer < 0) {
2584          // check that we're allowed to write to it
2585          if (tm->layerinfo[i].hidden) return;
2586          if (tm->layerinfo[i].locked == STBTE__locked) return;
2587          // if protected, dest has to be empty
2588          if (tm->layerinfo[i].locked == STBTE__protected && dest[i] != STBTE__BG(tm,i)) return;
2589          // if dragging w/o copy, we will try to erase stuff, which protection disallows
2590          if (dragging && tm->layerinfo[i].locked == STBTE__protected)
2591              return;
2592       }
2593       result[i] = dest[i];
2594       if (src[i] != STBTE__BG(tm,i))
2595          result[i] = src[i];
2596       return;
2597    }
2598 
2599    for (i=0; i < tm->num_layers; ++i) {
2600       result[i] = dest[i];
2601       if (src[i] != STBTE__NO_TILE)
2602          if (!tm->layerinfo[i].hidden && tm->layerinfo[i].locked != STBTE__locked)
2603             if (tm->layerinfo[i].locked == STBTE__unlocked || (!dragging && dest[i] == STBTE__BG(tm,i)))
2604                result[i] = src[i];
2605    }
2606 }
2607 
2608 // compute the result of dragging away from a tile
stbte__clear_stack(stbte_tilemap * tm,short result[])2609 static void stbte__clear_stack(stbte_tilemap *tm, short result[])
2610 {
2611    int i;
2612    // special case single-layer
2613    i = tm->cur_layer;
2614    if (tm->solo_layer >= 0)
2615       i = tm->solo_layer;
2616    if (i >= 0)
2617       result[i] = STBTE__BG(tm,i);
2618    else
2619       for (i=0; i < tm->num_layers; ++i)
2620          if (!tm->layerinfo[i].hidden && tm->layerinfo[i].locked == STBTE__unlocked)
2621             result[i] = STBTE__BG(tm,i);
2622 }
2623 
2624 // check if some map square is active
2625 #define STBTE__IS_MAP_ACTIVE()  ((stbte__ui.active_id & 127) == STBTE__map)
2626 #define STBTE__IS_MAP_HOT()     ((stbte__ui.hot_id & 127) == STBTE__map)
2627 
stbte__fillrect(stbte_tilemap * tm,int x0,int y0,int x1,int y1,int fill)2628 static void stbte__fillrect(stbte_tilemap *tm, int x0, int y0, int x1, int y1, int fill)
2629 {
2630    int i,j;
2631    int x=x0,y=y0;
2632 
2633    stbte__begin_undo(tm);
2634    if (x0 > x1) i=x0,x0=x1,x1=i;
2635    if (y0 > y1) j=y0,y0=y1,y1=j;
2636    for (j=y0; j <= y1; ++j)
2637       for (i=x0; i <= x1; ++i)
2638          if (fill)
2639             stbte__brush(tm, i,j);
2640          else
2641             stbte__erase(tm, i,j,STBTE__erase_any);
2642    stbte__end_undo(tm);
2643    // suppress warning from brush
2644    stbte__ui.alert_msg = 0;
2645 }
2646 
stbte__select_rect(stbte_tilemap * tm,int x0,int y0,int x1,int y1)2647 static void stbte__select_rect(stbte_tilemap *tm, int x0, int y0, int x1, int y1)
2648 {
2649    stbte__ui.has_selection = 1;
2650    stbte__ui.select_x0 = (x0 < x1 ? x0 : x1);
2651    stbte__ui.select_x1 = (x0 < x1 ? x1 : x0);
2652    stbte__ui.select_y0 = (y0 < y1 ? y0 : y1);
2653    stbte__ui.select_y1 = (y0 < y1 ? y1 : y0);
2654 }
2655 
stbte__copy_properties(float * dest,float * src)2656 static void stbte__copy_properties(float *dest, float *src)
2657 {
2658    int i;
2659    for (i=0; i < STBTE_MAX_PROPERTIES; ++i)
2660       dest[i] = src[i];
2661 }
2662 
stbte__copy_cut(stbte_tilemap * tm,int cut)2663 static void stbte__copy_cut(stbte_tilemap *tm, int cut)
2664 {
2665    int i,j,n,w,h,p=0;
2666    int copy_props = stbte__should_copy_properties(tm);
2667    if (!stbte__ui.has_selection)
2668       return;
2669    w = stbte__ui.select_x1 - stbte__ui.select_x0 + 1;
2670    h = stbte__ui.select_y1 - stbte__ui.select_y0 + 1;
2671    if (STBTE_MAX_COPY / w < h) {
2672       stbte__alert("Selection too large for copy buffer, increase STBTE_MAX_COPY");
2673       return;
2674    }
2675 
2676    for (i=0; i < w*h; ++i)
2677       for (n=0; n < tm->num_layers; ++n)
2678          stbte__ui.copybuffer[i][n] = STBTE__NO_TILE;
2679 
2680    if (cut)
2681       stbte__begin_undo(tm);
2682    for (j=stbte__ui.select_y0; j <= stbte__ui.select_y1; ++j) {
2683       for (i=stbte__ui.select_x0; i <= stbte__ui.select_x1; ++i) {
2684          for (n=0; n < tm->num_layers; ++n) {
2685             if (tm->solo_layer >= 0) {
2686                if (tm->solo_layer != n)
2687                   continue;
2688             } else {
2689                if (tm->cur_layer >= 0)
2690                   if (tm->cur_layer != n)
2691                      continue;
2692                if (tm->layerinfo[n].hidden)
2693                   continue;
2694                if (cut && tm->layerinfo[n].locked)
2695                   continue;
2696             }
2697             stbte__ui.copybuffer[p][n] = tm->data[j][i][n];
2698             if (cut) {
2699                stbte__undo_record(tm,i,j,n, tm->data[j][i][n]);
2700                tm->data[j][i][n] = (n==0 ? tm->background_tile : -1);
2701             }
2702          }
2703          if (copy_props) {
2704             stbte__copy_properties(stbte__ui.copyprops[p], tm->props[j][i]);
2705 #ifdef STBTE_ALLOW_LINK
2706             stbte__ui.copylinks[p] = tm->link[j][i];
2707             if (cut)
2708                stbte__set_link(tm, i,j,-1,-1, STBTE__undo_record);
2709 #endif
2710          }
2711          ++p;
2712       }
2713    }
2714    if (cut)
2715       stbte__end_undo(tm);
2716    stbte__ui.copy_width = w;
2717    stbte__ui.copy_height = h;
2718    stbte__ui.has_copy = 1;
2719    //stbte__ui.has_selection = 0;
2720    stbte__ui.copy_has_props = copy_props;
2721    stbte__ui.copy_src = tm; // used to give better semantics when copying links
2722    stbte__ui.copy_src_x = stbte__ui.select_x0;
2723    stbte__ui.copy_src_y = stbte__ui.select_y0;
2724 }
2725 
stbte__in_rect(int x,int y,int x0,int y0,int w,int h)2726 static int stbte__in_rect(int x, int y, int x0, int y0, int w, int h)
2727 {
2728    return x >= x0 && x < x0+w && y >= y0 && y < y0+h;
2729 }
2730 
stbte__in_src_rect(int x,int y)2731 static int stbte__in_src_rect(int x, int y)
2732 {
2733    return stbte__in_rect(x,y, stbte__ui.copy_src_x, stbte__ui.copy_src_y, stbte__ui.copy_width, stbte__ui.copy_height);
2734 }
2735 
stbte__in_dest_rect(int x,int y,int destx,int desty)2736 static int stbte__in_dest_rect(int x, int y, int destx, int desty)
2737 {
2738    return stbte__in_rect(x,y, destx, desty, stbte__ui.copy_width, stbte__ui.copy_height);
2739 }
2740 
stbte__paste(stbte_tilemap * tm,int mapx,int mapy)2741 static void stbte__paste(stbte_tilemap *tm, int mapx, int mapy)
2742 {
2743    int w = stbte__ui.copy_width;
2744    int h = stbte__ui.copy_height;
2745    int i,j,k,p;
2746    int x = mapx - (w>>1);
2747    int y = mapy - (h>>1);
2748    int copy_props = stbte__should_copy_properties(tm) && stbte__ui.copy_has_props;
2749    if (stbte__ui.has_copy == 0)
2750       return;
2751    stbte__begin_undo(tm);
2752    p = 0;
2753    for (j=0; j < h; ++j) {
2754       for (i=0; i < w; ++i) {
2755          if (y+j >= 0 && y+j < tm->max_y && x+i >= 0 && x+i < tm->max_x) {
2756             // compute the new stack
2757             short tilestack[STBTE_MAX_LAYERS];
2758             for (k=0; k < tm->num_layers; ++k)
2759                tilestack[k] = tm->data[y+j][x+i][k];
2760             stbte__paste_stack(tm, tilestack, tilestack, stbte__ui.copybuffer[p], 0);
2761             // update anything that changed
2762             for (k=0; k < tm->num_layers; ++k) {
2763                if (tilestack[k] != tm->data[y+j][x+i][k]) {
2764                   stbte__undo_record(tm, x+i,y+j,k, tm->data[y+j][x+i][k]);
2765                   tm->data[y+j][x+i][k] = tilestack[k];
2766                }
2767             }
2768          }
2769          if (copy_props) {
2770 #ifdef STBTE_ALLOW_LINK
2771             // need to decide how to paste a link, so there's a few cases
2772             int destx = -1, desty = -1;
2773             stbte__link *link = &stbte__ui.copylinks[p];
2774 
2775             // check if link is within-rect
2776             if (stbte__in_src_rect(link->x, link->y)) {
2777                // new link should point to copy (but only if copy is within map)
2778                destx = x + (link->x - stbte__ui.copy_src_x);
2779                desty = y + (link->y - stbte__ui.copy_src_y);
2780             } else if (tm == stbte__ui.copy_src) {
2781                // if same map, then preserve link unless target is overwritten
2782                if (!stbte__in_dest_rect(link->x,link->y,x,y)) {
2783                   destx = link->x;
2784                   desty = link->y;
2785                }
2786             }
2787             // this is necessary for offset-copy, but also in case max_x/max_y has changed
2788             if (destx < 0 || destx >= tm->max_x || desty < 0 || desty >= tm->max_y)
2789                destx = -1, desty = -1;
2790             stbte__set_link(tm, x+i, y+j, destx, desty, STBTE__undo_record);
2791 #endif
2792             for (k=0; k < STBTE_MAX_PROPERTIES; ++k) {
2793                if (tm->props[y+j][x+i][k] != stbte__ui.copyprops[p][k])
2794                   stbte__undo_record_prop_float(tm, x+i, y+j, k, tm->props[y+j][x+i][k]);
2795             }
2796             stbte__copy_properties(tm->props[y+j][x+i], stbte__ui.copyprops[p]);
2797          }
2798          ++p;
2799       }
2800    }
2801    stbte__end_undo(tm);
2802 }
2803 
stbte__drag_update(stbte_tilemap * tm,int mapx,int mapy,int copy_props)2804 static void stbte__drag_update(stbte_tilemap *tm, int mapx, int mapy, int copy_props)
2805 {
2806    int w = stbte__ui.drag_w, h = stbte__ui.drag_h;
2807    int ox,oy,i,deleted=0,written=0;
2808    short temp[STBTE_MAX_LAYERS];
2809    short *data = NULL;
2810    if (!stbte__ui.shift) {
2811       ox = mapx - stbte__ui.drag_x;
2812       oy = mapy - stbte__ui.drag_y;
2813       if (ox >= 0 && ox < w && oy >= 0 && oy < h) {
2814          deleted=1;
2815          for (i=0; i < tm->num_layers; ++i)
2816             temp[i] = tm->data[mapy][mapx][i];
2817          data = temp;
2818          stbte__clear_stack(tm, data);
2819       }
2820    }
2821    ox = mapx - stbte__ui.drag_dest_x;
2822    oy = mapy - stbte__ui.drag_dest_y;
2823    // if this map square is in the target drag region
2824    if (ox >= 0 && ox < w && oy >= 0 && oy < h) {
2825       // and the src map square is on the map
2826       if (stbte__in_rect(stbte__ui.drag_x+ox, stbte__ui.drag_y+oy, 0, 0, tm->max_x, tm->max_y)) {
2827          written = 1;
2828          if (data == NULL) {
2829             for (i=0; i < tm->num_layers; ++i)
2830                temp[i] = tm->data[mapy][mapx][i];
2831             data = temp;
2832          }
2833          stbte__paste_stack(tm, data, data, tm->data[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox], !stbte__ui.shift);
2834          if (copy_props) {
2835             for (i=0; i < STBTE_MAX_PROPERTIES; ++i) {
2836                if (tm->props[mapy][mapx][i] != tm->props[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox][i]) {
2837                   stbte__undo_record_prop_float(tm, mapx, mapy, i, tm->props[mapy][mapx][i]);
2838                   tm->props[mapy][mapx][i] = tm->props[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox][i];
2839                }
2840             }
2841          }
2842       }
2843    }
2844    if (data) {
2845       for (i=0; i < tm->num_layers; ++i) {
2846          if (tm->data[mapy][mapx][i] != data[i]) {
2847             stbte__undo_record(tm, mapx, mapy, i, tm->data[mapy][mapx][i]);
2848             tm->data[mapy][mapx][i] = data[i];
2849          }
2850       }
2851    }
2852    #ifdef STBTE_ALLOW_LINK
2853    if (copy_props) {
2854       int overwritten=0, moved=0, copied=0;
2855       // since this function is called on EVERY tile, we can fix up even tiles not
2856       // involved in the move
2857 
2858       stbte__link *k;
2859       // first, determine what src link ends up here
2860       k = &tm->link[mapy][mapx]; // by default, it's the one currently here
2861       if (deleted)               // if dragged away, it's erased
2862          k = NULL;
2863       if (written)               // if dragged into, it gets that link
2864          k = &tm->link[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox];
2865 
2866       // now check whether the *target* gets moved or overwritten
2867       if (k && k->x >= 0) {
2868          overwritten = stbte__in_rect(k->x, k->y, stbte__ui.drag_dest_x, stbte__ui.drag_dest_y, w, h);
2869          if (!stbte__ui.shift)
2870             moved    = stbte__in_rect(k->x, k->y, stbte__ui.drag_x     , stbte__ui.drag_y     , w, h);
2871          else
2872             copied   = stbte__in_rect(k->x, k->y, stbte__ui.drag_x     , stbte__ui.drag_y     , w, h);
2873       }
2874 
2875       if (deleted || written || overwritten || moved || copied) {
2876          // choose the final link value based on the above
2877          if (k == NULL || k->x < 0)
2878             stbte__set_link(tm, mapx, mapy, -1, -1, STBTE__undo_record);
2879          else if (moved || (copied && written)) {
2880             // if we move the target, we update to point to the new target;
2881             // or, if we copy the target and the source is part ofthe copy, then update to new target
2882             int x = k->x + (stbte__ui.drag_dest_x - stbte__ui.drag_x);
2883             int y = k->y + (stbte__ui.drag_dest_y - stbte__ui.drag_y);
2884             if (!(x >= 0 && y >= 0 && x < tm->max_x && y < tm->max_y))
2885                x = -1, y = -1;
2886             stbte__set_link(tm, mapx, mapy, x, y, STBTE__undo_record);
2887          } else if (overwritten) {
2888             stbte__set_link(tm, mapx, mapy, -1, -1, STBTE__undo_record);
2889          } else
2890             stbte__set_link(tm, mapx, mapy, k->x, k->y, STBTE__undo_record);
2891       }
2892    }
2893    #endif
2894 }
2895 
stbte__drag_place(stbte_tilemap * tm,int mapx,int mapy)2896 static void stbte__drag_place(stbte_tilemap *tm, int mapx, int mapy)
2897 {
2898    int i,j;
2899    int copy_props = stbte__should_copy_properties(tm);
2900    int move_x = (stbte__ui.drag_dest_x - stbte__ui.drag_x);
2901    int move_y = (stbte__ui.drag_dest_y - stbte__ui.drag_y);
2902    if (move_x == 0 && move_y == 0)
2903       return;
2904 
2905    stbte__begin_undo(tm);
2906    // we now need a 2D memmove-style mover that doesn't
2907    // overwrite any data as it goes. this requires being
2908    // direction sensitive in the same way as memmove
2909    if (move_y > 0 || (move_y == 0 && move_x > 0)) {
2910       for (j=tm->max_y-1; j >= 0; --j)
2911          for (i=tm->max_x-1; i >= 0; --i)
2912             stbte__drag_update(tm,i,j,copy_props);
2913    } else {
2914       for (j=0; j < tm->max_y; ++j)
2915          for (i=0; i < tm->max_x; ++i)
2916             stbte__drag_update(tm,i,j,copy_props);
2917    }
2918    stbte__end_undo(tm);
2919 
2920    stbte__ui.has_selection = 1;
2921    stbte__ui.select_x0 = stbte__ui.drag_dest_x;
2922    stbte__ui.select_y0 = stbte__ui.drag_dest_y;
2923    stbte__ui.select_x1 = stbte__ui.select_x0 + stbte__ui.drag_w - 1;
2924    stbte__ui.select_y1 = stbte__ui.select_y0 + stbte__ui.drag_h - 1;
2925 }
2926 
stbte__tile_paint(stbte_tilemap * tm,int sx,int sy,int mapx,int mapy,int layer)2927 static void stbte__tile_paint(stbte_tilemap *tm, int sx, int sy, int mapx, int mapy, int layer)
2928 {
2929    int i;
2930    int id = STBTE__IDMAP(mapx,mapy);
2931    int x0=sx, y0=sy;
2932    int x1=sx+tm->spacing_x, y1=sy+tm->spacing_y;
2933    int over = stbte__hittest(x0,y0,x1,y1, id);
2934    short *data = tm->data[mapy][mapx];
2935    short temp[STBTE_MAX_LAYERS];
2936 
2937    if (STBTE__IS_MAP_HOT()) {
2938       if (stbte__ui.pasting) {
2939          int ox = mapx - stbte__ui.paste_x;
2940          int oy = mapy - stbte__ui.paste_y;
2941          if (ox >= 0 && ox < stbte__ui.copy_width && oy >= 0 && oy < stbte__ui.copy_height) {
2942             stbte__paste_stack(tm, temp, tm->data[mapy][mapx], stbte__ui.copybuffer[oy*stbte__ui.copy_width+ox], 0);
2943             data = temp;
2944          }
2945       } else if (stbte__ui.dragging) {
2946          int ox,oy;
2947          for (i=0; i < tm->num_layers; ++i)
2948             temp[i] = tm->data[mapy][mapx][i];
2949          data = temp;
2950 
2951          // if it's in the source area, remove things unless shift-dragging
2952          ox = mapx - stbte__ui.drag_x;
2953          oy = mapy - stbte__ui.drag_y;
2954          if (!stbte__ui.shift && ox >= 0 && ox < stbte__ui.drag_w && oy >= 0 && oy < stbte__ui.drag_h) {
2955             stbte__clear_stack(tm, temp);
2956          }
2957 
2958          ox = mapx - stbte__ui.drag_dest_x;
2959          oy = mapy - stbte__ui.drag_dest_y;
2960          if (ox >= 0 && ox < stbte__ui.drag_w && oy >= 0 && oy < stbte__ui.drag_h) {
2961             stbte__paste_stack(tm, temp, temp, tm->data[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox], !stbte__ui.shift);
2962          }
2963       } else if (STBTE__IS_MAP_ACTIVE()) {
2964          if (stbte__ui.tool == STBTE__tool_rect) {
2965             if ((stbte__ui.ms_time & 511) < 380) {
2966                int ex = ((stbte__ui.hot_id >> 19) & 4095);
2967                int ey = ((stbte__ui.hot_id >>  7) & 4095);
2968                int sx = stbte__ui.sx;
2969                int sy = stbte__ui.sy;
2970 
2971                if (   ((mapx >= sx && mapx < ex+1) || (mapx >= ex && mapx < sx+1))
2972                    && ((mapy >= sy && mapy < ey+1) || (mapy >= ey && mapy < sy+1))) {
2973                   int i;
2974                   for (i=0; i < tm->num_layers; ++i)
2975                      temp[i] = tm->data[mapy][mapx][i];
2976                   data = temp;
2977                   if (stbte__ui.active_event == STBTE__leftdown)
2978                      stbte__brush_predict(tm, temp);
2979                   else
2980                      stbte__erase_predict(tm, temp, STBTE__erase_any);
2981                }
2982             }
2983          }
2984       }
2985    }
2986 
2987    if (STBTE__IS_HOT(id) && STBTE__INACTIVE() && !stbte__ui.pasting) {
2988       if (stbte__ui.tool == STBTE__tool_brush) {
2989          if ((stbte__ui.ms_time & 511) < 300) {
2990             data = temp;
2991             for (i=0; i < tm->num_layers; ++i)
2992                temp[i] = tm->data[mapy][mapx][i];
2993             stbte__brush_predict(tm, temp);
2994          }
2995       }
2996    }
2997 
2998    {
2999       i = layer;
3000       if (i == tm->solo_layer || (!tm->layerinfo[i].hidden && tm->solo_layer < 0))
3001          if (data[i] >= 0)
3002             STBTE_DRAW_TILE(x0,y0, (unsigned short) data[i], 0, tm->props[mapy][mapx]);
3003    }
3004 }
3005 
stbte__tile(stbte_tilemap * tm,int sx,int sy,int mapx,int mapy)3006 static void stbte__tile(stbte_tilemap *tm, int sx, int sy, int mapx, int mapy)
3007 {
3008    int tool = stbte__ui.tool;
3009    int x0=sx, y0=sy;
3010    int x1=sx+tm->spacing_x, y1=sy+tm->spacing_y;
3011    int id = STBTE__IDMAP(mapx,mapy);
3012    int over = stbte__hittest(x0,y0,x1,y1, id);
3013    switch (stbte__ui.event) {
3014       case STBTE__paint: {
3015          if (stbte__ui.pasting || stbte__ui.dragging || stbte__ui.scrolling)
3016             break;
3017          if (stbte__ui.scrollkey && !STBTE__IS_MAP_ACTIVE())
3018             break;
3019          if (STBTE__IS_HOT(id) && STBTE__IS_MAP_ACTIVE() && (tool == STBTE__tool_rect || tool == STBTE__tool_select)) {
3020             int rx0,ry0,rx1,ry1,t;
3021             // compute the center of each rect
3022             rx0 = x0 + tm->spacing_x/2;
3023             ry0 = y0 + tm->spacing_y/2;
3024             rx1 = rx0 + (stbte__ui.sx - mapx) * tm->spacing_x;
3025             ry1 = ry0 + (stbte__ui.sy - mapy) * tm->spacing_y;
3026             if (rx0 > rx1) t=rx0,rx0=rx1,rx1=t;
3027             if (ry0 > ry1) t=ry0,ry0=ry1,ry1=t;
3028             rx0 -= tm->spacing_x/2;
3029             ry0 -= tm->spacing_y/2;
3030             rx1 += tm->spacing_x/2;
3031             ry1 += tm->spacing_y/2;
3032             stbte__draw_frame(rx0-1,ry0-1,rx1+1,ry1+1, STBTE_COLOR_TILEMAP_HIGHLIGHT);
3033             break;
3034          }
3035          if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
3036             stbte__draw_frame(x0-1,y0-1,x1+1,y1+1, STBTE_COLOR_TILEMAP_HIGHLIGHT);
3037          }
3038 #ifdef STBTE_ALLOW_LINK
3039          if (stbte__ui.show_links && tm->link[mapy][mapx].x >= 0) {
3040             int tx = tm->link[mapy][mapx].x;
3041             int ty = tm->link[mapy][mapx].y;
3042             int lx0,ly0,lx1,ly1;
3043             if (STBTE_ALLOW_LINK(tm->data[mapy][mapx], tm->props[mapy][mapx],
3044                                  tm->data[ty  ][tx  ], tm->props[ty  ][tx  ]))
3045             {
3046                lx0 =  x0 + (tm->spacing_x >> 1) - 1;
3047                ly0 =  y0 + (tm->spacing_y >> 1) - 1;
3048                lx1 = lx0 + (tx - mapx) * tm->spacing_x + 2;
3049                ly1 = ly0 + (ty - mapy) * tm->spacing_y + 2;
3050                stbte__draw_link(lx0,ly0,lx1,ly1,
3051                    STBTE_LINK_COLOR(tm->data[mapy][mapx], tm->props[mapy][mapx],
3052                                     tm->data[ty  ][tx  ], tm->props[ty  ][tx]));
3053             }
3054          }
3055 #endif
3056          break;
3057       }
3058    }
3059 
3060    if (stbte__ui.pasting) {
3061       switch (stbte__ui.event) {
3062          case STBTE__leftdown:
3063             if (STBTE__IS_HOT(id)) {
3064                stbte__ui.pasting = 0;
3065                stbte__paste(tm, mapx, mapy);
3066                stbte__activate(0);
3067             }
3068             break;
3069          case STBTE__leftup:
3070             // just clear it no matter what, since they might click away to clear it
3071             stbte__activate(0);
3072             break;
3073          case STBTE__rightdown:
3074             if (STBTE__IS_HOT(id)) {
3075                stbte__activate(0);
3076                stbte__ui.pasting = 0;
3077             }
3078             break;
3079       }
3080       return;
3081    }
3082 
3083    if (stbte__ui.scrolling) {
3084       if (stbte__ui.event == STBTE__leftup) {
3085          stbte__activate(0);
3086          stbte__ui.scrolling = 0;
3087       }
3088       if (stbte__ui.event == STBTE__mousemove) {
3089          tm->scroll_x += (stbte__ui.start_x - stbte__ui.mx);
3090          tm->scroll_y += (stbte__ui.start_y - stbte__ui.my);
3091          stbte__ui.start_x = stbte__ui.mx;
3092          stbte__ui.start_y = stbte__ui.my;
3093       }
3094       return;
3095    }
3096 
3097    // regardless of tool, leftdown is a scrolldrag
3098    if (STBTE__IS_HOT(id) && stbte__ui.scrollkey && stbte__ui.event == STBTE__leftdown) {
3099       stbte__ui.scrolling = 1;
3100       stbte__ui.start_x = stbte__ui.mx;
3101       stbte__ui.start_y = stbte__ui.my;
3102       return;
3103    }
3104 
3105    switch (tool) {
3106       case STBTE__tool_brush:
3107          switch (stbte__ui.event) {
3108             case STBTE__mousemove:
3109                if (STBTE__IS_MAP_ACTIVE() && over) {
3110                   // don't brush/erase same tile multiple times unless they move away and back @TODO should just be only once, but that needs another data structure
3111                   if (!STBTE__IS_ACTIVE(id)) {
3112                      if (stbte__ui.active_event == STBTE__leftdown)
3113                         stbte__brush(tm, mapx, mapy);
3114                      else
3115                         stbte__erase(tm, mapx, mapy, stbte__ui.brush_state);
3116                      stbte__ui.active_id = id; // switch to this map square so we don't rebrush IT multiple times
3117                   }
3118                }
3119                break;
3120             case STBTE__leftdown:
3121                if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
3122                   stbte__activate(id);
3123                   stbte__begin_undo(tm);
3124                   stbte__brush(tm, mapx, mapy);
3125                }
3126                break;
3127             case STBTE__rightdown:
3128                if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
3129                   stbte__activate(id);
3130                   stbte__begin_undo(tm);
3131                   if (stbte__erase(tm, mapx, mapy, STBTE__erase_any) == STBTE__erase_brushonly)
3132                      stbte__ui.brush_state = STBTE__erase_brushonly;
3133                   else
3134                      stbte__ui.brush_state = STBTE__erase_any;
3135                }
3136                break;
3137             case STBTE__leftup:
3138             case STBTE__rightup:
3139                if (STBTE__IS_MAP_ACTIVE()) {
3140                   stbte__end_undo(tm);
3141                   stbte__activate(0);
3142                }
3143                break;
3144          }
3145          break;
3146 
3147 #ifdef STBTE_ALLOW_LINK
3148       case STBTE__tool_link:
3149          switch (stbte__ui.event) {
3150             case STBTE__leftdown:
3151                if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
3152                   stbte__activate(id);
3153                   stbte__ui.linking = 1;
3154                   stbte__ui.sx = mapx;
3155                   stbte__ui.sy = mapy;
3156                   // @TODO: undo
3157                }
3158                break;
3159             case STBTE__leftup:
3160                if (STBTE__IS_HOT(id) && STBTE__IS_MAP_ACTIVE()) {
3161                   if ((mapx != stbte__ui.sx || mapy != stbte__ui.sy) &&
3162                          STBTE_ALLOW_LINK(tm->data[stbte__ui.sy][stbte__ui.sx], tm->props[stbte__ui.sy][stbte__ui.sx],
3163                                           tm->data[mapy][mapx], tm->props[mapy][mapx]))
3164                      stbte__set_link(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, STBTE__undo_block);
3165                   else
3166                      stbte__set_link(tm, stbte__ui.sx, stbte__ui.sy, -1,-1, STBTE__undo_block);
3167                   stbte__ui.linking = 0;
3168                   stbte__activate(0);
3169                }
3170                break;
3171 
3172             case STBTE__rightdown:
3173                if (STBTE__IS_ACTIVE(id)) {
3174                   stbte__activate(0);
3175                   stbte__ui.linking = 0;
3176                }
3177                break;
3178          }
3179          break;
3180 #endif
3181 
3182       case STBTE__tool_erase:
3183          switch (stbte__ui.event) {
3184             case STBTE__mousemove:
3185                if (STBTE__IS_MAP_ACTIVE() && over)
3186                   stbte__erase(tm, mapx, mapy, STBTE__erase_all);
3187                break;
3188             case STBTE__leftdown:
3189                if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
3190                   stbte__activate(id);
3191                   stbte__begin_undo(tm);
3192                   stbte__erase(tm, mapx, mapy, STBTE__erase_all);
3193                }
3194                break;
3195             case STBTE__leftup:
3196                if (STBTE__IS_MAP_ACTIVE()) {
3197                   stbte__end_undo(tm);
3198                   stbte__activate(0);
3199                }
3200                break;
3201          }
3202          break;
3203 
3204       case STBTE__tool_select:
3205          if (STBTE__IS_HOT(id)) {
3206             switch (stbte__ui.event) {
3207                case STBTE__leftdown:
3208                   if (STBTE__INACTIVE()) {
3209                      // if we're clicking in an existing selection...
3210                      if (stbte__ui.has_selection) {
3211                         if (  mapx >= stbte__ui.select_x0 && mapx <= stbte__ui.select_x1
3212                            && mapy >= stbte__ui.select_y0 && mapy <= stbte__ui.select_y1)
3213                         {
3214                            stbte__ui.dragging = 1;
3215                            stbte__ui.drag_x = stbte__ui.select_x0;
3216                            stbte__ui.drag_y = stbte__ui.select_y0;
3217                            stbte__ui.drag_w = stbte__ui.select_x1 - stbte__ui.select_x0 + 1;
3218                            stbte__ui.drag_h = stbte__ui.select_y1 - stbte__ui.select_y0 + 1;
3219                            stbte__ui.drag_offx = mapx - stbte__ui.select_x0;
3220                            stbte__ui.drag_offy = mapy - stbte__ui.select_y0;
3221                         }
3222                      }
3223                      stbte__ui.has_selection = 0; // no selection until it completes
3224                      stbte__activate_map(mapx,mapy);
3225                   }
3226                   break;
3227                case STBTE__leftup:
3228                   if (STBTE__IS_MAP_ACTIVE()) {
3229                      if (stbte__ui.dragging) {
3230                         stbte__drag_place(tm, mapx,mapy);
3231                         stbte__ui.dragging = 0;
3232                         stbte__activate(0);
3233                      } else {
3234                         stbte__select_rect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy);
3235                         stbte__activate(0);
3236                      }
3237                   }
3238                   break;
3239                case STBTE__rightdown:
3240                   stbte__ui.has_selection = 0;
3241                   break;
3242             }
3243          }
3244          break;
3245 
3246       case STBTE__tool_rect:
3247          if (STBTE__IS_HOT(id)) {
3248             switch (stbte__ui.event) {
3249                case STBTE__leftdown:
3250                   if (STBTE__INACTIVE())
3251                      stbte__activate_map(mapx,mapy);
3252                   break;
3253                case STBTE__leftup:
3254                   if (STBTE__IS_MAP_ACTIVE()) {
3255                      stbte__fillrect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, 1);
3256                      stbte__activate(0);
3257                   }
3258                   break;
3259                case STBTE__rightdown:
3260                   if (STBTE__INACTIVE())
3261                      stbte__activate_map(mapx,mapy);
3262                   break;
3263                case STBTE__rightup:
3264                   if (STBTE__IS_MAP_ACTIVE()) {
3265                      stbte__fillrect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, 0);
3266                      stbte__activate(0);
3267                   }
3268                   break;
3269             }
3270          }
3271          break;
3272 
3273 
3274       case STBTE__tool_eyedrop:
3275          switch (stbte__ui.event) {
3276             case STBTE__leftdown:
3277                if (STBTE__IS_HOT(id) && STBTE__INACTIVE())
3278                   stbte__eyedrop(tm,mapx,mapy);
3279                break;
3280          }
3281          break;
3282    }
3283 }
3284 
stbte__start_paste(stbte_tilemap * tm)3285 static void stbte__start_paste(stbte_tilemap *tm)
3286 {
3287    if (stbte__ui.has_copy) {
3288       stbte__ui.pasting = 1;
3289       stbte__activate(STBTE__ID(STBTE__toolbarB,3));
3290    }
3291 }
3292 
stbte__toolbar(stbte_tilemap * tm,int x0,int y0,int w,int h)3293 static void stbte__toolbar(stbte_tilemap *tm, int x0, int y0, int w, int h)
3294 {
3295    int i;
3296    int estimated_width = 13 * STBTE__num_tool + 8+8+ 120+4 - 30;
3297    int x = x0 + w/2 - estimated_width/2;
3298    int y = y0+1;
3299 
3300    for (i=0; i < STBTE__num_tool; ++i) {
3301       int highlight=0, disable=0;
3302       highlight = (stbte__ui.tool == i);
3303       if (i == STBTE__tool_undo || i == STBTE__tool_showgrid)
3304           x += 8;
3305       if (i == STBTE__tool_showgrid && stbte__ui.show_grid)
3306          highlight = 1;
3307       if (i == STBTE__tool_showlinks && stbte__ui.show_links)
3308          highlight = 1;
3309       if (i == STBTE__tool_fill)
3310          continue;
3311       #ifndef STBTE_ALLOW_LINK
3312       if (i == STBTE__tool_link || i == STBTE__tool_showlinks)
3313          disable = 1;
3314       #endif
3315       if (i == STBTE__tool_undo && !stbte__undo_available(tm))
3316          disable = 1;
3317       if (i == STBTE__tool_redo && !stbte__redo_available(tm))
3318          disable = 1;
3319       if (stbte__button_icon(STBTE__ctoolbar_button, toolchar[i], x, y, 13, STBTE__ID(STBTE__toolbarA, i), highlight, disable)) {
3320          switch (i) {
3321             case STBTE__tool_eyedrop:
3322                stbte__ui.eyedrop_last_layer = tm->num_layers; // flush eyedropper state
3323                // fallthrough
3324             default:
3325                stbte__ui.tool = i;
3326                stbte__ui.has_selection = 0;
3327                break;
3328             case STBTE__tool_showlinks:
3329                stbte__ui.show_links = !stbte__ui.show_links;
3330                break;
3331             case STBTE__tool_showgrid:
3332                stbte__ui.show_grid = (stbte__ui.show_grid+1)%3;
3333                break;
3334             case STBTE__tool_undo:
3335                stbte__undo(tm);
3336                break;
3337             case STBTE__tool_redo:
3338                stbte__redo(tm);
3339                break;
3340          }
3341       }
3342       x += 13;
3343    }
3344 
3345    x += 8;
3346    if (stbte__button(STBTE__ctoolbar_button, "cut"  , x, y,10, 40, STBTE__ID(STBTE__toolbarB,0), 0, !stbte__ui.has_selection))
3347       stbte__copy_cut(tm, 1);
3348    x += 42;
3349    if (stbte__button(STBTE__ctoolbar_button, "copy" , x, y, 5, 40, STBTE__ID(STBTE__toolbarB,1), 0, !stbte__ui.has_selection))
3350       stbte__copy_cut(tm, 0);
3351    x += 42;
3352    if (stbte__button(STBTE__ctoolbar_button, "paste", x, y, 0, 40, STBTE__ID(STBTE__toolbarB,2), stbte__ui.pasting, !stbte__ui.has_copy))
3353       stbte__start_paste(tm);
3354 }
3355 
3356 #define STBTE__TEXTCOLOR(n)  stbte__color_table[n][STBTE__text][STBTE__idle]
3357 
stbte__info_value(const char * label,int x,int y,int val,int digits,int id)3358 static int stbte__info_value(const char *label, int x, int y, int val, int digits, int id)
3359 {
3360    if (stbte__ui.event == STBTE__paint) {
3361       int off = 9-stbte__get_char_width(label[0]);
3362       char text[16];
3363       sprintf(text, label, digits, val);
3364       stbte__draw_text_core(x+off,y, text, 999, STBTE__TEXTCOLOR(STBTE__cpanel),1);
3365    }
3366    if (id) {
3367       x += 9+7*digits+4;
3368       if (stbte__minibutton(STBTE__cmapsize, x,y, '+', STBTE__ID2(id,1,0)))
3369          val += (stbte__ui.shift ? 10 : 1);
3370       x += 9;
3371       if (stbte__minibutton(STBTE__cmapsize, x,y, '-', STBTE__ID2(id,2,0)))
3372          val -= (stbte__ui.shift ? 10 : 1);
3373       if (val < 1) val = 1; else if (val > 4096) val = 4096;
3374    }
3375    return val;
3376 }
3377 
stbte__info(stbte_tilemap * tm,int x0,int y0,int w,int h)3378 static void stbte__info(stbte_tilemap *tm, int x0, int y0, int w, int h)
3379 {
3380    int mode = stbte__ui.panel[STBTE__panel_info].mode;
3381    int s = 11+7*tm->digits+4+15;
3382    int x,y;
3383    int in_region;
3384 
3385    x = x0+2;
3386    y = y0+2;
3387    tm->max_x = stbte__info_value("w:%*d",x,y, tm->max_x, tm->digits, STBTE__ID(STBTE__info,0));
3388    if (mode)
3389       x += s;
3390    else
3391       y += 11;
3392    tm->max_y = stbte__info_value("h:%*d",x,y, tm->max_y, tm->digits, STBTE__ID(STBTE__info,1));
3393    x = x0+2;
3394    y += 11;
3395    in_region = (stbte__ui.hot_id & 127) == STBTE__map;
3396    stbte__info_value(in_region ? "x:%*d" : "x:",x,y, (stbte__ui.hot_id>>19)&4095, tm->digits, 0);
3397    if (mode)
3398       x += s;
3399    else
3400       y += 11;
3401    stbte__info_value(in_region ? "y:%*d" : "y:",x,y, (stbte__ui.hot_id>> 7)&4095, tm->digits, 0);
3402    y += 15;
3403    x = x0+2;
3404    stbte__draw_text(x,y,"brush:",40,STBTE__TEXTCOLOR(STBTE__cpanel));
3405    if (tm->cur_tile >= 0)
3406       STBTE_DRAW_TILE(x+43,y-3,tm->tiles[tm->cur_tile].id,1,0);
3407 }
3408 
stbte__layers(stbte_tilemap * tm,int x0,int y0,int w,int h)3409 static void stbte__layers(stbte_tilemap *tm, int x0, int y0, int w, int h)
3410 {
3411    static char *propmodes[3] = {
3412       "default", "always", "never"
3413    };
3414    int num_rows;
3415    int i, y, n;
3416    int x1 = x0+w;
3417    int y1 = y0+h;
3418    int xoff = 20;
3419 
3420    if (tm->has_layer_names) {
3421       int side = stbte__ui.panel[STBTE__panel_layers].side;
3422       xoff = stbte__region[side].width - 42;
3423       xoff = (xoff < tm->layername_width + 10 ? xoff : tm->layername_width + 10);
3424    }
3425 
3426    x0 += 2;
3427    y0 += 5;
3428    if (!tm->has_layer_names) {
3429       if (stbte__ui.event == STBTE__paint) {
3430          stbte__draw_text(x0,y0, "Layers", w-4, STBTE__TEXTCOLOR(STBTE__cpanel));
3431       }
3432       y0 += 11;
3433    }
3434    num_rows = (y1-y0)/15;
3435 #ifndef STBTE_NO_PROPS
3436    --num_rows;
3437 #endif
3438    y = y0;
3439    for (i=0; i < tm->num_layers; ++i) {
3440       char text[3], *str = (char *) tm->layerinfo[i].name;
3441       static char lockedchar[3] = { 'U', 'P', 'L' };
3442       int locked = tm->layerinfo[i].locked;
3443       int disabled = (tm->solo_layer >= 0 && tm->solo_layer != i);
3444       if (i-tm->layer_scroll >= 0 && i-tm->layer_scroll < num_rows) {
3445          if (str == NULL)
3446             sprintf(str=text, "%2d", i+1);
3447          if (stbte__button(STBTE__clayer_button, str, x0,y,(i+1<10)*2,xoff-2, STBTE__ID(STBTE__layer,i), tm->cur_layer==i,0))
3448             tm->cur_layer = (tm->cur_layer == i ? -1 : i);
3449          if (stbte__layerbutton(x0+xoff +  0,y+1,'H',STBTE__ID(STBTE__hide,i), tm->layerinfo[i].hidden,disabled,STBTE__clayer_hide))
3450             tm->layerinfo[i].hidden = !tm->layerinfo[i].hidden;
3451          if (stbte__layerbutton(x0+xoff + 12,y+1,lockedchar[locked],STBTE__ID(STBTE__lock,i), locked!=0,disabled,STBTE__clayer_lock))
3452             tm->layerinfo[i].locked = (locked+1)%3;
3453          if (stbte__layerbutton(x0+xoff + 24,y+1,'S',STBTE__ID(STBTE__solo,i), tm->solo_layer==i,0,STBTE__clayer_solo))
3454             tm->solo_layer = (tm->solo_layer == i ? -1 : i);
3455          y += 15;
3456       }
3457    }
3458    stbte__scrollbar(x1-4, y0,y-2, &tm->layer_scroll, 0, tm->num_layers, num_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__layer));
3459 #ifndef STBTE_NO_PROPS
3460    n = stbte__text_width("prop:")+2;
3461    stbte__draw_text(x0,y+2, "prop:", w, STBTE__TEXTCOLOR(STBTE__cpanel));
3462    i = w - n - 4;
3463    if (i > 50) i = 50;
3464    if (stbte__button(STBTE__clayer_button, propmodes[tm->propmode], x0+n,y,0,i, STBTE__ID(STBTE__layer,256), 0,0))
3465       tm->propmode = (tm->propmode+1)%3;
3466 #endif
3467 }
3468 
stbte__categories(stbte_tilemap * tm,int x0,int y0,int w,int h)3469 static void stbte__categories(stbte_tilemap *tm, int x0, int y0, int w, int h)
3470 {
3471    int s=11, x,y, i;
3472    int num_rows = h / s;
3473 
3474    w -= 4;
3475    x = x0+2;
3476    y = y0+4;
3477    if (tm->category_scroll == 0) {
3478       if (stbte__category_button("*ALL*", x,y, w, STBTE__ID(STBTE__categories, 65535), tm->cur_category == -1)) {
3479          stbte__choose_category(tm, -1);
3480       }
3481       y += s;
3482    }
3483 
3484    for (i=0; i < tm->num_categories; ++i) {
3485       if (i+1 - tm->category_scroll >= 0 && i+1 - tm->category_scroll < num_rows) {
3486          if (y + 10 > y0+h)
3487             return;
3488          if (stbte__category_button(tm->categories[i], x,y,w, STBTE__ID(STBTE__categories,i), tm->cur_category == i))
3489             stbte__choose_category(tm, i);
3490          y += s;
3491       }
3492    }
3493    stbte__scrollbar(x0+w, y0+4, y0+h-4, &tm->category_scroll, 0, tm->num_categories+1, num_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__categories));
3494 }
3495 
stbte__tile_in_palette(stbte_tilemap * tm,int x,int y,int slot)3496 static void stbte__tile_in_palette(stbte_tilemap *tm, int x, int y, int slot)
3497 {
3498    stbte__tileinfo *t = &tm->tiles[slot];
3499    int x0=x, y0=y, x1 = x+tm->palette_spacing_x - 1, y1 = y+tm->palette_spacing_y;
3500    int id = STBTE__ID(STBTE__palette, slot);
3501    int over = stbte__hittest(x0,y0,x1,y1, id);
3502    switch (stbte__ui.event) {
3503       case STBTE__paint:
3504          stbte__draw_rect(x,y,x+tm->palette_spacing_x-1,y+tm->palette_spacing_x-1, STBTE_COLOR_TILEPALETTE_BACKGROUND);
3505          STBTE_DRAW_TILE(x,y,t->id, slot == tm->cur_tile,0);
3506          if (slot == tm->cur_tile)
3507             stbte__draw_frame_delayed(x-1,y-1,x+tm->palette_spacing_x,y+tm->palette_spacing_y, STBTE_COLOR_TILEPALETTE_OUTLINE);
3508          break;
3509       default:
3510          if (stbte__button_core(id))
3511             tm->cur_tile = slot;
3512          break;
3513    }
3514 }
3515 
stbte__palette_of_tiles(stbte_tilemap * tm,int x0,int y0,int w,int h)3516 static void stbte__palette_of_tiles(stbte_tilemap *tm, int x0, int y0, int w, int h)
3517 {
3518    int i,x,y;
3519    int num_vis_rows = (h-6) / tm->palette_spacing_y;
3520    int num_columns = (w-2-6) / tm->palette_spacing_x;
3521    int num_total_rows;
3522    int column,row;
3523    int x1 = x0+w, y1=y0+h;
3524    x = x0+2;
3525    y = y0+6;
3526 
3527    if (num_columns == 0)
3528       return;
3529 
3530    num_total_rows = (tm->cur_palette_count + num_columns-1) / num_columns; // ceil()
3531 
3532    column = 0;
3533    row    = -tm->palette_scroll;
3534    for (i=0; i < tm->num_tiles; ++i) {
3535       stbte__tileinfo *t = &tm->tiles[i];
3536 
3537       // filter based on category
3538       if (tm->cur_category >= 0 && t->category_id != tm->cur_category)
3539          continue;
3540 
3541       // display it
3542       if (row >= 0 && row < num_vis_rows) {
3543          x = x0 + 2 + tm->palette_spacing_x * column;
3544          y = y0 + 6 + tm->palette_spacing_y * row;
3545          stbte__tile_in_palette(tm,x,y,i);
3546       }
3547 
3548       ++column;
3549       if (column == num_columns) {
3550          column = 0;
3551          ++row;
3552       }
3553    }
3554    stbte__flush_delay();
3555    stbte__scrollbar(x1-4, y0+6, y1-2, &tm->palette_scroll, 0, num_total_rows, num_vis_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__palette));
3556 }
3557 
stbte__linear_remap(float n,float x0,float x1,float y0,float y1)3558 static float stbte__linear_remap(float n, float x0, float x1, float y0, float y1)
3559 {
3560    return (n-x0)/(x1-x0)*(y1-y0) + y0;
3561 }
3562 
3563 static float stbte__saved;
stbte__props_panel(stbte_tilemap * tm,int x0,int y0,int w,int h)3564 static void stbte__props_panel(stbte_tilemap *tm, int x0, int y0, int w, int h)
3565 {
3566    int x1 = x0+w, y1 = y0+h;
3567    int i;
3568    int y = y0 + 5, x = x0+2;
3569    int slider_width = 60;
3570    int mx,my;
3571    float *p;
3572    short *data;
3573    if (!stbte__is_single_selection())
3574       return;
3575    mx = stbte__ui.select_x0;
3576    my = stbte__ui.select_y0;
3577    p = tm->props[my][mx];
3578    data = tm->data[my][mx];
3579    for (i=0; i < STBTE_MAX_PROPERTIES; ++i) {
3580       unsigned int n = STBTE_PROP_TYPE(i, data, p);
3581       if (n) {
3582          char *s = STBTE_PROP_NAME(i, data, p);
3583          if (s == NULL) s = "";
3584          switch (n & 3) {
3585             case STBTE_PROP_bool: {
3586                int flag = (int) p[i];
3587                if (stbte__layerbutton(x,y, flag ? 'x' : ' ', STBTE__ID(STBTE__prop_flag,i), flag, 0, 2)) {
3588                   stbte__begin_undo(tm);
3589                   stbte__undo_record_prop_float(tm,mx,my,i,(float) flag);
3590                   p[i] = (float) !flag;
3591                   stbte__end_undo(tm);
3592                }
3593                stbte__draw_text(x+13,y+1,s,x1-(x+13)-2,STBTE__TEXTCOLOR(STBTE__cpanel));
3594                y += 13;
3595                break;
3596             }
3597             case STBTE_PROP_int: {
3598                int a = (int) STBTE_PROP_MIN(i,data,p);
3599                int b = (int) STBTE_PROP_MAX(i,data,p);
3600                int v = (int) p[i] - a;
3601                if (a+v != p[i] || v < 0 || v > b-a) {
3602                   if (v < 0) v = 0;
3603                   if (v > b-a) v = b-a;
3604                   p[i] = (float) (a+v); // @TODO undo
3605                }
3606                switch (stbte__slider(x, slider_width, y+7, b-a, &v, STBTE__ID(STBTE__prop_int,i)))
3607                {
3608                   case STBTE__begin:
3609                      stbte__saved = p[i];
3610                      // fallthrough
3611                   case STBTE__change:
3612                      p[i] = (float) (a+v); // @TODO undo
3613                      break;
3614                   case STBTE__end:
3615                      if (p[i] != stbte__saved) {
3616                         stbte__begin_undo(tm);
3617                         stbte__undo_record_prop_float(tm,mx,my,i,stbte__saved);
3618                         stbte__end_undo(tm);
3619                      }
3620                      break;
3621                }
3622                stbte__draw_text(x+slider_width+2,y+2, s, x1-1-(x+slider_width+2), STBTE__TEXTCOLOR(STBTE__cpanel));
3623                y += 12;
3624                break;
3625             }
3626             case STBTE_PROP_float: {
3627                float a = (float) STBTE_PROP_MIN(i, data,p);
3628                float b = (float) STBTE_PROP_MAX(i, data,p);
3629                float c = STBTE_PROP_FLOAT_SCALE(i, data, p);
3630                float old;
3631                if (p[i] < a || p[i] > b) {
3632                   // @TODO undo
3633                   if (p[i] < a) p[i] = a;
3634                   if (p[i] > b) p[i] = b;
3635                }
3636                old = p[i];
3637                switch (stbte__float_control(x, y, 50, a, b, c, "%8.4f", &p[i], STBTE__layer,STBTE__ID(STBTE__prop_float,i))) {
3638                   case STBTE__begin:
3639                      stbte__saved = old;
3640                      break;
3641                   case STBTE__end:
3642                      if (stbte__saved != p[i]) {
3643                         stbte__begin_undo(tm);
3644                         stbte__undo_record_prop_float(tm,mx,my,i, stbte__saved);
3645                         stbte__end_undo(tm);
3646                      }
3647                      break;
3648                }
3649                stbte__draw_text(x+53,y+1, s, x1-1-(x+53), STBTE__TEXTCOLOR(STBTE__cpanel));
3650                y += 12;
3651                break;
3652             }
3653          }
3654       }
3655    }
3656 }
3657 
3658 static int stbte__cp_mode, stbte__cp_aspect, stbte__cp_state, stbte__cp_index, stbte__save, stbte__cp_altered, stbte__color_copy;
3659 #ifdef STBTE__COLORPICKER
stbte__dump_colorstate(void)3660 static void stbte__dump_colorstate(void)
3661 {
3662    int i,j,k;
3663    printf("static int stbte__color_table[STBTE__num_color_modes][STBTE__num_color_aspects][STBTE__num_color_states] =\n");
3664    printf("{\n");
3665    printf("   {\n");
3666    for (k=0; k < STBTE__num_color_modes; ++k) {
3667       for (j=0; j < STBTE__num_color_aspects; ++j) {
3668          printf("      { ");
3669          for (i=0; i < STBTE__num_color_states; ++i) {
3670             printf("0x%06x, ", stbte__color_table[k][j][i]);
3671          }
3672          printf("},\n");
3673       }
3674       if (k+1 < STBTE__num_color_modes)
3675          printf("   }, {\n");
3676       else
3677          printf("   },\n");
3678    }
3679    printf("};\n");
3680 }
3681 
stbte__colorpicker(int x0,int y0,int w,int h)3682 static void stbte__colorpicker(int x0, int y0, int w, int h)
3683 {
3684    int x1 = x0+w, y1 = y0+h, x,y, i;
3685 
3686    x =  x0+2; y = y0+6;
3687 
3688    y += 5;
3689    x += 8;
3690 
3691 
3692    {
3693       int color = stbte__color_table[stbte__cp_mode][stbte__cp_aspect][stbte__cp_index];
3694       int rgb[3];
3695       if (stbte__cp_altered && stbte__cp_index == STBTE__idle)
3696          color = stbte__save;
3697 
3698       if (stbte__minibutton(STBTE__cmapsize, x1-20,y+ 5, 'C', STBTE__ID2(STBTE__colorpick_id,4,0)))
3699          stbte__color_copy = color;
3700       if (stbte__minibutton(STBTE__cmapsize, x1-20,y+15, 'P', STBTE__ID2(STBTE__colorpick_id,4,1)))
3701          color = stbte__color_copy;
3702 
3703       rgb[0] = color >> 16; rgb[1] = (color>>8)&255; rgb[2] = color & 255;
3704       for (i=0; i < 3; ++i) {
3705          if (stbte__slider(x+8,64, y, 255, rgb+i, STBTE__ID2(STBTE__colorpick_id,3,i)) > 0)
3706             stbte__dump_colorstate();
3707          y += 15;
3708       }
3709       if (stbte__ui.event != STBTE__paint && stbte__ui.event != STBTE__tick)
3710          stbte__color_table[stbte__cp_mode][stbte__cp_aspect][stbte__cp_index] = (rgb[0]<<16)|(rgb[1]<<8)|(rgb[2]);
3711    }
3712 
3713    y += 5;
3714 
3715    // states
3716    x = x0+2+35;
3717    if (stbte__ui.event == STBTE__paint) {
3718       static char *states[] = { "idle", "over", "down", "down&over", "selected", "selected&over", "disabled" };
3719       stbte__draw_text(x, y+1, states[stbte__cp_index], x1-x-1, 0xffffff);
3720    }
3721 
3722    x = x0+24; y += 12;
3723 
3724    for (i=3; i >= 0; --i) {
3725       int state = 0 != (stbte__cp_state & (1 << i));
3726       if (stbte__layerbutton(x,y, "OASD"[i], STBTE__ID2(STBTE__colorpick_id, 0,i), state,0, STBTE__clayer_button)) {
3727          stbte__cp_state ^= (1 << i);
3728          stbte__cp_index = stbte__state_to_index[0][0][0][stbte__cp_state];
3729       }
3730       x += 16;
3731    }
3732    x = x0+2; y += 18;
3733 
3734    for (i=0; i < 3; ++i) {
3735       static char *labels[] = { "Base", "Edge", "Text" };
3736       if (stbte__button(STBTE__ctoolbar_button, labels[i], x,y,0,36, STBTE__ID2(STBTE__colorpick_id,1,i), stbte__cp_aspect==i,0))
3737          stbte__cp_aspect = i;
3738       x += 40;
3739    }
3740 
3741    y += 18;
3742    x = x0+2;
3743 
3744    for (i=0; i < STBTE__num_color_modes; ++i) {
3745       if (stbte__button(STBTE__ctoolbar_button, stbte__color_names[i], x, y, 0,80, STBTE__ID2(STBTE__colorpick_id,2,i), stbte__cp_mode == i,0))
3746          stbte__cp_mode = i;
3747       y += 12;
3748    }
3749 
3750    // make the currently selected aspect flash, unless we're actively dragging color slider etc
3751    if (stbte__ui.event == STBTE__tick) {
3752       stbte__save = stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle];
3753       if ((stbte__ui.active_id & 127) != STBTE__colorpick_id) {
3754          if ((stbte__ui.ms_time & 2047) < 200) {
3755             stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle] ^= 0x1f1f1f;
3756             stbte__cp_altered = 1;
3757          }
3758       }
3759    }
3760 }
3761 #endif
3762 
stbte__editor_traverse(stbte_tilemap * tm)3763 static void stbte__editor_traverse(stbte_tilemap *tm)
3764 {
3765    int i,j,i0,j0,i1,j1,n;
3766 
3767    if (tm == NULL)
3768       return;
3769    if (stbte__ui.x0 == stbte__ui.x1 || stbte__ui.y0 == stbte__ui.y1)
3770       return;
3771 
3772    stbte__prepare_tileinfo(tm);
3773 
3774    stbte__compute_panel_locations(tm); // @OPTIMIZE: we don't need to recompute this every time
3775 
3776    if (stbte__ui.event == STBTE__paint) {
3777       // fill screen with border
3778       stbte__draw_rect(stbte__ui.x0, stbte__ui.y0, stbte__ui.x1, stbte__ui.y1, STBTE_COLOR_TILEMAP_BORDER);
3779       // fill tilemap with tilemap background
3780       stbte__draw_rect(stbte__ui.x0 - tm->scroll_x, stbte__ui.y0 - tm->scroll_y,
3781                        stbte__ui.x0 - tm->scroll_x + tm->spacing_x * tm->max_x,
3782                        stbte__ui.y0 - tm->scroll_y + tm->spacing_y * tm->max_y, STBTE_COLOR_TILEMAP_BACKGROUND);
3783    }
3784 
3785    // step 1: traverse all the tilemap data...
3786 
3787    i0 = (tm->scroll_x - tm->spacing_x) / tm->spacing_x;
3788    j0 = (tm->scroll_y - tm->spacing_y) / tm->spacing_y;
3789    i1 = (tm->scroll_x + stbte__ui.x1 - stbte__ui.x0) / tm->spacing_x + 1;
3790    j1 = (tm->scroll_y + stbte__ui.y1 - stbte__ui.y0) / tm->spacing_y + 1;
3791 
3792    if (i0 < 0) i0 = 0;
3793    if (j0 < 0) j0 = 0;
3794    if (i1 > tm->max_x) i1 = tm->max_x;
3795    if (j1 > tm->max_y) j1 = tm->max_y;
3796 
3797    if (stbte__ui.event == STBTE__paint) {
3798       // draw all of layer 0, then all of layer 1, etc, instead of old
3799       // way which drew entire stack of each tile at once
3800       for (n=0; n < tm->num_layers; ++n) {
3801          for (j=j0; j < j1; ++j) {
3802             for (i=i0; i < i1; ++i) {
3803                int x = stbte__ui.x0 + i * tm->spacing_x - tm->scroll_x;
3804                int y = stbte__ui.y0 + j * tm->spacing_y - tm->scroll_y;
3805                stbte__tile_paint(tm, x, y, i, j, n);
3806             }
3807          }
3808          if (n == 0 && stbte__ui.show_grid == 1) {
3809             int x = stbte__ui.x0 + i0 * tm->spacing_x - tm->scroll_x;
3810             int y = stbte__ui.y0 + j0 * tm->spacing_y - tm->scroll_y;
3811             for (i=0; x < stbte__ui.x1 && i <= i1; ++i, x += tm->spacing_x)
3812                stbte__draw_rect(x, stbte__ui.y0, x+1, stbte__ui.y1, STBTE_COLOR_GRID);
3813             for (j=0; y < stbte__ui.y1 && j <= j1; ++j, y += tm->spacing_y)
3814                stbte__draw_rect(stbte__ui.x0, y, stbte__ui.x1, y+1, STBTE_COLOR_GRID);
3815          }
3816       }
3817    }
3818 
3819    if (stbte__ui.event == STBTE__paint) {
3820       // draw grid on top of everything except UI
3821       if (stbte__ui.show_grid == 2) {
3822          int x = stbte__ui.x0 + i0 * tm->spacing_x - tm->scroll_x;
3823          int y = stbte__ui.y0 + j0 * tm->spacing_y - tm->scroll_y;
3824          for (i=0; x < stbte__ui.x1 && i <= i1; ++i, x += tm->spacing_x)
3825             stbte__draw_rect(x, stbte__ui.y0, x+1, stbte__ui.y1, STBTE_COLOR_GRID);
3826          for (j=0; y < stbte__ui.y1 && j <= j1; ++j, y += tm->spacing_y)
3827             stbte__draw_rect(stbte__ui.x0, y, stbte__ui.x1, y+1, STBTE_COLOR_GRID);
3828       }
3829    }
3830 
3831    for (j=j0; j < j1; ++j) {
3832       for (i=i0; i < i1; ++i) {
3833          int x = stbte__ui.x0 + i * tm->spacing_x - tm->scroll_x;
3834          int y = stbte__ui.y0 + j * tm->spacing_y - tm->scroll_y;
3835          stbte__tile(tm, x, y, i, j);
3836       }
3837    }
3838 
3839    if (stbte__ui.event == STBTE__paint) {
3840       // draw the selection border
3841       if (stbte__ui.has_selection) {
3842          int x0,y0,x1,y1;
3843          x0 = stbte__ui.x0 + (stbte__ui.select_x0    ) * tm->spacing_x - tm->scroll_x;
3844          y0 = stbte__ui.y0 + (stbte__ui.select_y0    ) * tm->spacing_y - tm->scroll_y;
3845          x1 = stbte__ui.x0 + (stbte__ui.select_x1 + 1) * tm->spacing_x - tm->scroll_x + 1;
3846          y1 = stbte__ui.y0 + (stbte__ui.select_y1 + 1) * tm->spacing_y - tm->scroll_y + 1;
3847          stbte__draw_frame(x0,y0,x1,y1, (stbte__ui.ms_time & 256 ? STBTE_COLOR_SELECTION_OUTLINE1 : STBTE_COLOR_SELECTION_OUTLINE2));
3848       }
3849 
3850       stbte__flush_delay(); // draw a dynamic link on top of the queued links
3851 
3852       #ifdef STBTE_ALLOW_LINK
3853       if (stbte__ui.linking && STBTE__IS_MAP_HOT()) {
3854          int x0,y0,x1,y1;
3855          int color;
3856          int ex = ((stbte__ui.hot_id >> 19) & 4095);
3857          int ey = ((stbte__ui.hot_id >>  7) & 4095);
3858          x0 = stbte__ui.x0 + (stbte__ui.sx    ) * tm->spacing_x - tm->scroll_x + (tm->spacing_x>>1)+1;
3859          y0 = stbte__ui.y0 + (stbte__ui.sy    ) * tm->spacing_y - tm->scroll_y + (tm->spacing_y>>1)+1;
3860          x1 = stbte__ui.x0 + (ex              ) * tm->spacing_x - tm->scroll_x + (tm->spacing_x>>1)-1;
3861          y1 = stbte__ui.y0 + (ey              ) * tm->spacing_y - tm->scroll_y + (tm->spacing_y>>1)-1;
3862          if (STBTE_ALLOW_LINK(tm->data[stbte__ui.sy][stbte__ui.sx], tm->props[stbte__ui.sy][stbte__ui.sx], tm->data[ey][ex], tm->props[ey][ex]))
3863             color = STBTE_LINK_COLOR_DRAWING;
3864          else
3865             color = STBTE_LINK_COLOR_DISALLOWED;
3866          stbte__draw_link(x0,y0,x1,y1, color);
3867       }
3868       #endif
3869    }
3870    stbte__flush_delay();
3871 
3872    // step 2: traverse the panels
3873    for (i=0; i < STBTE__num_panel; ++i) {
3874       stbte__panel *p = &stbte__ui.panel[i];
3875       if (stbte__ui.event == STBTE__paint) {
3876          stbte__draw_box(p->x0,p->y0,p->x0+p->width,p->y0+p->height, STBTE__cpanel, STBTE__idle);
3877       }
3878       // obscure tilemap data underneath panel
3879       stbte__hittest(p->x0,p->y0,p->x0+p->width,p->y0+p->height, STBTE__ID2(STBTE__panel, i, 0));
3880       switch (i) {
3881          case STBTE__panel_toolbar:
3882             if (stbte__ui.event == STBTE__paint)
3883                stbte__draw_rect(p->x0,p->y0,p->x0+p->width,p->y0+p->height, stbte__color_table[STBTE__ctoolbar][STBTE__base][STBTE__idle]);
3884             stbte__toolbar(tm,p->x0,p->y0,p->width,p->height);
3885             break;
3886          case STBTE__panel_info:
3887             stbte__info(tm,p->x0,p->y0,p->width,p->height);
3888             break;
3889          case STBTE__panel_layers:
3890             stbte__layers(tm,p->x0,p->y0,p->width,p->height);
3891             break;
3892          case STBTE__panel_categories:
3893             stbte__categories(tm,p->x0,p->y0,p->width,p->height);
3894             break;
3895          case STBTE__panel_colorpick:
3896 #ifdef STBTE__COLORPICKER
3897             stbte__colorpicker(p->x0,p->y0,p->width,p->height);
3898 #endif
3899             break;
3900          case STBTE__panel_tiles:
3901             // erase boundary between categories and tiles if they're on same side
3902             if (stbte__ui.event == STBTE__paint && p->side == stbte__ui.panel[STBTE__panel_categories].side)
3903                stbte__draw_rect(p->x0+1,p->y0-1,p->x0+p->width-1,p->y0+1, stbte__color_table[STBTE__cpanel][STBTE__base][STBTE__idle]);
3904             stbte__palette_of_tiles(tm,p->x0,p->y0,p->width,p->height);
3905             break;
3906          case STBTE__panel_props:
3907             stbte__props_panel(tm,p->x0,p->y0,p->width,p->height);
3908             break;
3909       }
3910       // draw the panel side selectors
3911       for (j=0; j < 2; ++j) {
3912          int result;
3913          if (i == STBTE__panel_toolbar) continue;
3914          result = stbte__microbutton(p->x0+p->width - 1 - 2*4 + 4*j,p->y0+2,3, STBTE__ID2(STBTE__panel, i, j+1), STBTE__cpanel_sider+j);
3915          if (result) {
3916             switch (j) {
3917                case 0: p->side = result > 0 ? STBTE__side_left : STBTE__side_right; break;
3918                case 1: p->delta_height += result; break;
3919             }
3920          }
3921       }
3922    }
3923 
3924    if (stbte__ui.panel[STBTE__panel_categories].delta_height < -5) stbte__ui.panel[STBTE__panel_categories].delta_height = -5;
3925    if (stbte__ui.panel[STBTE__panel_layers    ].delta_height < -5) stbte__ui.panel[STBTE__panel_layers    ].delta_height = -5;
3926 
3927 
3928    // step 3: traverse the regions to place expander controls on them
3929    for (i=0; i < 2; ++i) {
3930       if (stbte__region[i].active) {
3931          int x = stbte__region[i].x;
3932          int width;
3933          if (i == STBTE__side_left)
3934             width =  stbte__ui.left_width , x += stbte__region[i].width + 1;
3935          else
3936             width = -stbte__ui.right_width, x -= 6;
3937          if (stbte__microbutton_dragger(x, stbte__region[i].y+2, 5, STBTE__ID(STBTE__region,i), &width)) {
3938             // if non-0, it is expanding, so retract it
3939             if (stbte__region[i].retracted == 0.0)
3940                stbte__region[i].retracted = 0.01f;
3941             else
3942                stbte__region[i].retracted = 0.0;
3943          }
3944          if (i == STBTE__side_left)
3945             stbte__ui.left_width  =  width;
3946          else
3947             stbte__ui.right_width = -width;
3948          if (stbte__ui.event == STBTE__tick) {
3949             if (stbte__region[i].retracted && stbte__region[i].retracted < 1.0f) {
3950                stbte__region[i].retracted += stbte__ui.dt*4;
3951                if (stbte__region[i].retracted > 1)
3952                   stbte__region[i].retracted = 1;
3953             }
3954          }
3955       }
3956    }
3957 
3958    if (stbte__ui.event == STBTE__paint && stbte__ui.alert_msg) {
3959       int w = stbte__text_width(stbte__ui.alert_msg);
3960       int x = (stbte__ui.x0+stbte__ui.x1)/2;
3961       int y = (stbte__ui.y0+stbte__ui.y1)*5/6;
3962       stbte__draw_rect (x-w/2-4,y-8, x+w/2+4,y+8, 0x604020);
3963       stbte__draw_frame(x-w/2-4,y-8, x+w/2+4,y+8, 0x906030);
3964       stbte__draw_text (x-w/2,y-4, stbte__ui.alert_msg, w+1, 0xff8040);
3965    }
3966 
3967 #ifdef STBTE_SHOW_CURSOR
3968    if (stbte__ui.event == STBTE__paint)
3969       stbte__draw_bitmap(stbte__ui.mx, stbte__ui.my, stbte__get_char_width(26), stbte__get_char_bitmap(26), 0xe0e0e0);
3970 #endif
3971 
3972    if (stbte__ui.event == STBTE__tick && stbte__ui.alert_msg) {
3973       stbte__ui.alert_timer -= stbte__ui.dt;
3974       if (stbte__ui.alert_timer < 0) {
3975          stbte__ui.alert_timer = 0;
3976          stbte__ui.alert_msg = 0;
3977       }
3978    }
3979 
3980    if (stbte__ui.event == STBTE__paint) {
3981       stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle] = stbte__save;
3982       stbte__cp_altered = 0;
3983    }
3984 }
3985 
stbte__do_event(stbte_tilemap * tm)3986 static void stbte__do_event(stbte_tilemap *tm)
3987 {
3988    stbte__ui.next_hot_id = 0;
3989    stbte__editor_traverse(tm);
3990    stbte__ui.hot_id = stbte__ui.next_hot_id;
3991 
3992    // automatically cancel on mouse-up in case the object that triggered it
3993    // doesn't exist anymore
3994    if (stbte__ui.active_id) {
3995       if (stbte__ui.event == STBTE__leftup || stbte__ui.event == STBTE__rightup) {
3996          if (!stbte__ui.pasting) {
3997             stbte__activate(0);
3998             if (stbte__ui.undoing)
3999                stbte__end_undo(tm);
4000             stbte__ui.scrolling = 0;
4001             stbte__ui.dragging = 0;
4002             stbte__ui.linking = 0;
4003          }
4004       }
4005    }
4006 
4007    // we could do this stuff in the widgets directly, but it would keep recomputing
4008    // the same thing on every tile, which seems dumb.
4009 
4010    if (stbte__ui.pasting) {
4011       if (STBTE__IS_MAP_HOT()) {
4012          // compute pasting location based on last hot
4013          stbte__ui.paste_x = ((stbte__ui.hot_id >> 19) & 4095) - (stbte__ui.copy_width >> 1);
4014          stbte__ui.paste_y = ((stbte__ui.hot_id >>  7) & 4095) - (stbte__ui.copy_height >> 1);
4015       }
4016    }
4017    if (stbte__ui.dragging) {
4018       if (STBTE__IS_MAP_HOT()) {
4019          stbte__ui.drag_dest_x = ((stbte__ui.hot_id >> 19) & 4095) - stbte__ui.drag_offx;
4020          stbte__ui.drag_dest_y = ((stbte__ui.hot_id >>  7) & 4095) - stbte__ui.drag_offy;
4021       }
4022    }
4023 }
4024 
stbte__set_event(int event,int x,int y)4025 static void stbte__set_event(int event, int x, int y)
4026 {
4027    stbte__ui.event = event;
4028    stbte__ui.mx    = x;
4029    stbte__ui.my    = y;
4030    stbte__ui.dx    = x - stbte__ui.last_mouse_x;
4031    stbte__ui.dy    = y - stbte__ui.last_mouse_y;
4032    stbte__ui.last_mouse_x = x;
4033    stbte__ui.last_mouse_y = y;
4034    stbte__ui.accum_x += stbte__ui.dx;
4035    stbte__ui.accum_y += stbte__ui.dy;
4036 }
4037 
stbte_draw(stbte_tilemap * tm)4038 void stbte_draw(stbte_tilemap *tm)
4039 {
4040    stbte__ui.event = STBTE__paint;
4041    stbte__editor_traverse(tm);
4042 }
4043 
stbte_mouse_move(stbte_tilemap * tm,int x,int y,int shifted,int scrollkey)4044 void stbte_mouse_move(stbte_tilemap *tm, int x, int y, int shifted, int scrollkey)
4045 {
4046    stbte__set_event(STBTE__mousemove, x,y);
4047    stbte__ui.shift = shifted;
4048    stbte__ui.scrollkey = scrollkey;
4049    stbte__do_event(tm);
4050 }
4051 
stbte_mouse_button(stbte_tilemap * tm,int x,int y,int right,int down,int shifted,int scrollkey)4052 void stbte_mouse_button(stbte_tilemap *tm, int x, int y, int right, int down, int shifted, int scrollkey)
4053 {
4054    static int events[2][2] = { { STBTE__leftup , STBTE__leftdown  },
4055                                { STBTE__rightup, STBTE__rightdown } };
4056    stbte__set_event(events[right][down], x,y);
4057    stbte__ui.shift = shifted;
4058    stbte__ui.scrollkey = scrollkey;
4059 
4060    stbte__do_event(tm);
4061 }
4062 
stbte_mouse_wheel(stbte_tilemap * tm,int x,int y,int vscroll)4063 void stbte_mouse_wheel(stbte_tilemap *tm, int x, int y, int vscroll)
4064 {
4065    // not implemented yet -- need different way of hittesting
4066 }
4067 
stbte_action(stbte_tilemap * tm,enum stbte_action act)4068 void stbte_action(stbte_tilemap *tm, enum stbte_action act)
4069 {
4070    switch (act) {
4071       case STBTE_tool_select:      stbte__ui.tool = STBTE__tool_select;               break;
4072       case STBTE_tool_brush:       stbte__ui.tool = STBTE__tool_brush;                break;
4073       case STBTE_tool_erase:       stbte__ui.tool = STBTE__tool_erase;                break;
4074       case STBTE_tool_rectangle:   stbte__ui.tool = STBTE__tool_rect;                 break;
4075       case STBTE_tool_eyedropper:  stbte__ui.tool = STBTE__tool_eyedrop;              break;
4076       case STBTE_tool_link:        stbte__ui.tool = STBTE__tool_link;                 break;
4077       case STBTE_act_toggle_grid:  stbte__ui.show_grid = (stbte__ui.show_grid+1) % 3; break;
4078       case STBTE_act_toggle_links: stbte__ui.show_links ^= 1;                         break;
4079       case STBTE_act_undo:         stbte__undo(tm);                                   break;
4080       case STBTE_act_redo:         stbte__redo(tm);                                   break;
4081       case STBTE_act_cut:          stbte__copy_cut(tm, 1);                            break;
4082       case STBTE_act_copy:         stbte__copy_cut(tm, 0);                            break;
4083       case STBTE_act_paste:        stbte__start_paste(tm);                            break;
4084       case STBTE_scroll_left:      tm->scroll_x -= tm->spacing_x;                     break;
4085       case STBTE_scroll_right:     tm->scroll_x += tm->spacing_x;                     break;
4086       case STBTE_scroll_up:        tm->scroll_y -= tm->spacing_y;                     break;
4087       case STBTE_scroll_down:      tm->scroll_y += tm->spacing_y;                     break;
4088    }
4089 }
4090 
stbte_tick(stbte_tilemap * tm,float dt)4091 void stbte_tick(stbte_tilemap *tm, float dt)
4092 {
4093    stbte__ui.event = STBTE__tick;
4094    stbte__ui.dt    = dt;
4095    stbte__do_event(tm);
4096    stbte__ui.ms_time += (int) (dt * 1024) + 1; // make sure if time is superfast it always updates a little
4097 }
4098 
stbte_mouse_sdl(stbte_tilemap * tm,const void * sdl_event,float xs,float ys,int xo,int yo)4099 void stbte_mouse_sdl(stbte_tilemap *tm, const void *sdl_event, float xs, float ys, int xo, int yo)
4100 {
4101 #ifdef _SDL_H
4102    SDL_Event *event = (SDL_Event *) sdl_event;
4103    SDL_Keymod km = SDL_GetModState();
4104    int shift = (km & KMOD_LCTRL) || (km & KMOD_RCTRL);
4105    int scrollkey = 0 != SDL_GetKeyboardState(NULL)[SDL_SCANCODE_SPACE];
4106    switch (event->type) {
4107       case SDL_MOUSEMOTION:
4108          stbte_mouse_move(tm, (int) (xs*event->motion.x+xo), (int) (ys*event->motion.y+yo), shift, scrollkey);
4109          break;
4110       case SDL_MOUSEBUTTONUP:
4111          stbte_mouse_button(tm, (int) (xs*event->button.x+xo), (int) (ys*event->button.y+yo), event->button.button != SDL_BUTTON_LEFT, 0, shift, scrollkey);
4112          break;
4113       case SDL_MOUSEBUTTONDOWN:
4114          stbte_mouse_button(tm, (int) (xs*event->button.x+xo), (int) (ys*event->button.y+yo), event->button.button != SDL_BUTTON_LEFT, 1, shift, scrollkey);
4115          break;
4116       case SDL_MOUSEWHEEL:
4117          stbte_mouse_wheel(tm, stbte__ui.mx, stbte__ui.my, event->wheel.y);
4118          break;
4119    }
4120 #else
4121    STBTE__NOTUSED(tm);
4122    STBTE__NOTUSED(sdl_event);
4123    STBTE__NOTUSED(xs);
4124    STBTE__NOTUSED(ys);
4125    STBTE__NOTUSED(xo);
4126    STBTE__NOTUSED(yo);
4127 #endif
4128 }
4129 
4130 #endif // STB_TILEMAP_EDITOR_IMPLEMENTATION
4131 
4132 /*
4133 ------------------------------------------------------------------------------
4134 This software is available under 2 licenses -- choose whichever you prefer.
4135 ------------------------------------------------------------------------------
4136 ALTERNATIVE A - MIT License
4137 Copyright (c) 2017 Sean Barrett
4138 Permission is hereby granted, free of charge, to any person obtaining a copy of
4139 this software and associated documentation files (the "Software"), to deal in
4140 the Software without restriction, including without limitation the rights to
4141 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
4142 of the Software, and to permit persons to whom the Software is furnished to do
4143 so, subject to the following conditions:
4144 The above copyright notice and this permission notice shall be included in all
4145 copies or substantial portions of the Software.
4146 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4147 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4148 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4149 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4150 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
4151 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
4152 SOFTWARE.
4153 ------------------------------------------------------------------------------
4154 ALTERNATIVE B - Public Domain (www.unlicense.org)
4155 This is free and unencumbered software released into the public domain.
4156 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
4157 software, either in source code form or as a compiled binary, for any purpose,
4158 commercial or non-commercial, and by any means.
4159 In jurisdictions that recognize copyright laws, the author or authors of this
4160 software dedicate any and all copyright interest in the software to the public
4161 domain. We make this dedication for the benefit of the public at large and to
4162 the detriment of our heirs and successors. We intend this dedication to be an
4163 overt act of relinquishment in perpetuity of all present and future rights to
4164 this software under copyright law.
4165 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4166 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4167 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4168 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
4169 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
4170 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
4171 ------------------------------------------------------------------------------
4172 */
4173