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