ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/crossedit/Edit.c
Revision: 1.1.1.1 (vendor branch)
Committed: Fri Feb 3 07:11:44 2006 UTC (18 years, 3 months ago) by root
Content type: text/plain
Branch: UPSTREAM
CVS Tags: UPSTREAM_2006_03_15, UPSTREAM_2006_02_22, UPSTREAM_2006_02_03
Changes since 1.1: +0 -0 lines
Log Message:
initial import

File Contents

# Content
1 /*
2 * CrossEdit - game world editor
3 * Copyright (C) 1993 Jarkko Sonninen & Petri Heinila
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 * The authors can be reached via e-mail to Jarkko.Sonninen@lut.fi
20 * or Petri.Heinila@lut.fi .
21 */
22
23 #include "Defines.h"
24 #include "Edit.h"
25
26 #include "X11.h"
27 #include "Ansi.h"
28
29 #include "debug.h"
30
31 #include "CrList.h"
32 #include "CrEdit.h"
33
34 #include "Cnv.h"
35 /* HACK Make sure we know how big CnvPromptMax is. */
36 #include "Cnv/config.h"
37 #include "App.h"
38 #include "Attr.h"
39 #include "MapAttr.h"
40 #include "Str.h"
41 #include "Bitmaps.h"
42
43
44 /*
45 * This function relinks all _pointers_ to the objects from
46 * one map to another.
47 * Note: You can _not_ free the objects in the original map
48 * after this function has been called.
49 * (What happened to this function? It no longer copies the pointers! -Frank)
50 * moved to Edit.c from common/map.c since Edit.c is the only file that uses it.
51 */
52
53 void copy_map(mapstruct *m1, mapstruct *m2) {
54 int x,y;
55
56 memcpy(m2, m1, sizeof(mapstruct));
57
58 for(x=0;x<MAP_WIDTH(m1)&&x<MAP_WIDTH(m2);x++)
59 for(y=0;y<MAP_HEIGHT(m1)&&y<MAP_HEIGHT(m2);y++) {
60 SET_MAP_FACE(m2,x,y,GET_MAP_FACE(m1,x,y,0),0);
61 SET_MAP_FACE(m2,x,y,GET_MAP_FACE(m1,x,y,1),1);
62 SET_MAP_FACE(m2,x,y,GET_MAP_FACE(m1,x,y,2),2);
63 }
64 }
65
66 /*
67 * member: copy by translate objects from source to a new map
68 * source: -map
69 * width : width of target map
70 * height: height of target map
71 * dx : positive translate to right
72 * dy : positive translate to down
73 */
74 mapstruct *MapMoveScrollResize(mapstruct *source,
75 int width, int height, int dx, int dy)
76 {
77 mapstruct *target;
78 object *obj,*prt; /* PaRT of obj */
79 int x,y,sx = MAP_WIDTH(source), sy = MAP_HEIGHT(source);
80 int linked = 0, link=0;
81 int i;
82
83 if (!width) width = sx;
84 if (!height) height = sy;
85 target = get_empty_map (width, height);
86
87 if(dx < 0) dx += MAP_WIDTH(target);
88 if(dy < 0) dy += MAP_HEIGHT(target);
89
90 /*
91 * Copy the map-headers from source to target.
92 *
93 * The name, msg and tiling paths are allocated for
94 * each map (see map.c::load_map_header()). Copy
95 * the pointer, then NULL the pointer in source so
96 * the strings aren't freed.
97 *
98 * Statements below in the order the fields appear
99 * in map.h.
100 */
101
102 #define MOVE_AND_CLEAR(to, from) to = from; from = NULL;
103 strncpy (target->path, source->path, HUGE_BUF - 1);
104 target->path[HUGE_BUF - 1] = '\0';
105 MOVE_AND_CLEAR(target->tmpname, source->tmpname);
106 MOVE_AND_CLEAR(target->name, source->name);
107 target->region = source->region;
108 MAP_WHEN_RESET(target) = MAP_WHEN_RESET(source);
109 MAP_RESET_TIMEOUT(target) = MAP_RESET_TIMEOUT(source);
110 target->fixed_resettime = source->fixed_resettime;
111 target->unique = source->unique;
112 MAP_NOSMOOTH(target) = MAP_NOSMOOTH(source);
113 MAP_TIMEOUT(target) = MAP_TIMEOUT(source);
114 MAP_SWAP_TIME(target) = MAP_SWAP_TIME(source);
115 /* fields players/in_memory not copied */
116 target->compressed = source->compressed;
117 MAP_DIFFICULTY(target) = MAP_DIFFICULTY(source);
118 MAP_DARKNESS(target) = MAP_DARKNESS(source);
119 MAP_WIDTH(target) = width;
120 MAP_HEIGHT(target) = height;
121 MAP_ENTER_X(target) = (MAP_ENTER_X(source) + dx) % width;
122 MAP_ENTER_Y(target) = (MAP_ENTER_Y(source) + dy) % height;
123 target->outdoor = MAP_OUTDOORS(source);
124 MAP_TEMP(target) = MAP_TEMP(source);
125 MAP_PRESSURE(target) = MAP_PRESSURE(source);
126 MAP_HUMID(target) = MAP_HUMID(source);
127 MAP_WINDSPEED(target) = MAP_WINDSPEED(source);
128 MAP_WINDDIRECTION(target) = MAP_WINDDIRECTION(source);
129 MAP_SKYCOND(target) = MAP_SKYCOND(source);
130 /* fields wpartx/wparty not copied */
131 MOVE_AND_CLEAR(target->msg, source->msg)
132 /* Tiling paths. */
133 for (i = 0; i < 4; i++) {
134 MOVE_AND_CLEAR(target->tile_path[i], source->tile_path[i]);
135 }
136 #undef MOVE_AND_CLEAR
137
138 for(y=0; y < sy && y < MAP_HEIGHT(target); y++)
139 for(x=0; x < sx && x < MAP_WIDTH(target); x++)
140 while((obj = get_map_ob(source,x,y)) && !obj->head) {
141 if ((linked = QUERY_FLAG (obj,FLAG_IS_LINKED))) {
142 link = get_button_value (obj);
143 remove_button_link (obj);
144 }
145 remove_ob(obj);
146 for(prt = obj; prt; prt = prt->more) {
147 prt->x += dx;
148 prt->x %= MAP_WIDTH(target); /* it can be split by edge */
149 prt->y += dy; /* designers problem to fix */
150 prt->y %= MAP_HEIGHT(target);
151 }
152 insert_ob_in_map(obj,target,obj,INS_NO_MERGE | INS_NO_WALK_ON);
153 if (linked)
154 add_button_link(obj, target, link);
155 }
156 /*free_all_objects(source);*/
157 free_map (source, 1);
158 delete_map (source);
159 return target;
160 }
161
162
163 object * MapGetRealObject (mapstruct * emap, int x, int y, int z)
164 {
165 object *tmp = MapGetObjectZ (emap, x, y, z);
166 return tmp ? (tmp->head ? tmp->head : tmp) : tmp;
167 }
168
169 int MapInsertObjectZ(mapstruct *emap,object *o,int x, int y, int z)
170 {
171 object *op, *above, *below;
172
173 if (o->more)
174 MapInsertObjectZ (emap,o->more, x, y, z);
175
176 o->x += x;
177 o->y += y;
178 o->map = emap;
179 CLEAR_FLAG(o,FLAG_REMOVED);
180
181 op = get_map_ob (emap, o->x, o->y);
182 if (z < 0) {
183 above = op;
184 below = NULL;
185 } else {
186 while (op && op->above)
187 op = op->above;
188
189 above = NULL;
190 below = op;
191 while (op && z-- > 0) {
192 above = op;
193 below = op = op->below;
194 }
195 }
196 o->below = below;
197 o->above = above;
198
199 if (above)
200 above->below = o;
201 else {
202 SET_MAP_FACE (emap, o->x, o->y, o->face,0);
203 }
204 if (below)
205 below->above = o;
206 else
207 set_map_ob (emap, o->x, o->y, o);
208
209 return (0);
210 }
211
212 int MapObjectOut (mapstruct *target, object *obj, int x, int y) {
213 object *tmp;
214 for(tmp = obj; tmp; tmp = tmp->more)
215 if(OUT_OF_REAL_MAP(target,x + tmp->x,y + tmp->y)) return 1;
216 return 0;
217 }
218
219 object * MapGetObjectZ (mapstruct * emap, int x, int y, int z)
220 {
221 object *op;
222
223 if (!emap || out_of_map (emap, x, y))
224 return (NULL);
225 op = get_map_ob (emap, x, y);
226 while (op && op->above)
227 op = op->above;
228 while (op && z-- > 0)
229 op = op->below;
230 return (op);
231 }
232
233
234
235
236 /**********************************************************************
237 * inner declarations
238 **********************************************************************/
239
240 /*
241 static void CbEditSetPath (Widget w, XtPointer client, XtPointer call);
242 static void EditRefreshCb (Widget w, XtPointer client, XtPointer call);
243 static void CbEditStart (Widget w, XtPointer client, XtPointer call);
244
245 static void CbEditToggleRead (Widget w, XtPointer client, XtPointer call);
246 static void CbEditToggleOver (Widget w, XtPointer client, XtPointer call);
247 static void CbEditToggleAuto (Widget w, XtPointer client, XtPointer call);
248 */
249 /*
250 static Boolean EdFreeMap ( Edit *);
251 static Boolean Load ( Edit *, char *name );
252 static int EdSelectItem ( Edit *, int x, int y );
253 */
254 static Boolean EdSaveMap (Edit self, char *name);
255 /*
256 static void EditResizeScroll(Edit self,int width,int height,int dx,int dy);
257 */
258 static void EditInsertArch (Edit self, int x, int y, int i, archetype * at);
259
260 object *EditCloneInsert (Edit self,object *obj,int x, int y, int z);
261 Boolean EditObjectDelete (Edit self, int x, int y, int z);
262
263 /*
264 * to all refresh
265 */
266 const XRectangle EditRectAll = {
267 0,0,
268 10000,10000
269 };
270
271 /**********************************************************************
272 * privates
273 **********************************************************************/
274
275
276 /*
277 *
278 */
279 static Boolean EdFreeMap (Edit self)
280 {
281 if (!self) return False;
282
283 /*** no item from self map anymore ***/
284 if (AppItemGetEdit(self->app) == self) {
285 AppItemSet (self->app, NULL,NULL,0);
286 }
287 if(self->app->look.edit == self)
288 AppSelectUnset(self->app);
289 if (self->emap) {
290 *self->emap->path = '\0';
291 free_map (self->emap, 1);
292 delete_map (self->emap);
293 self->emap = NULL;
294 }
295 return True;
296 }
297
298 /*
299 * member : load map from file to memory
300 * there should no other map loading function for editor
301 * than self
302 * name : filename of map, relative ( level number )
303 * return : True, if map is loaded
304 */
305 static Boolean Load(Edit self, char *name)
306 {
307 int mask;
308 mapstruct *tmp;
309 char path[PATH_MAX+1],save[PATH_MAX+1];
310 char buf[BUFSIZ];
311
312 strcpy(path, name);
313 if((mask = check_path (path,1)) < 4) {
314 sprintf(buf,"You can't access %s",path);
315 CnvNotify(buf,"Continue",NULL);
316 return False;
317 }
318
319 /* new map, no refs */
320 if(self->app->look.edit == self) {
321 AppSelectUnset(self->app);
322 AttrChange(self->app->attr,NULL, 0, 0);
323 }
324 /*** remove old mapstructure ***/
325 if(self->emap)
326 strcpy(save,self->emap->path); /* klydge */
327 EdFreeMap (self);
328 if(!(tmp = ready_map_name (path, MAP_FLUSH))) {
329 /* Make the map null, since right now we don't have a valid map
330 * in memory. The CnfNotify call will force a redraw - we
331 * can check for Null map data pretty easy, but can't really check
332 * for unknown map data.
333 */
334 if (self->w) /* only when already a window, open otherwhise coredump */
335 XtVaSetValues(self->w, XtNmap, NULL, NULL);
336 sprintf(buf,"Cannot load map %s",path);
337 CnvNotify(buf,"Continue",NULL);
338 if(!(tmp = ready_map_name(save,MAP_FLUSH))) return False;
339 }
340
341 /*** initialiaze map ***/
342 debug1("Edit-Load() %s\n",path);
343 if(self->view) XawViewportSetLocation(self->view,0,0);
344 self->emap = tmp;
345 self->read_only = mask & 2 ? 0 : 1;
346 strcpy (self->emap->path, path);
347 EditUnmodified(self);
348 EditUpdate(self);
349 CrEditRefresh(self->w,EditRectAll);
350 return True;
351 }
352
353 /**********************************************************************
354 * wall handling
355 **********************************************************************/
356
357 static int direction_x[] = {0, -1, 0, 1};
358 static int direction_y[] = {1, 0, -1, 0};
359
360 /*
361 *
362 */
363 static int find_draw_arch (mapstruct * emap, int line, archetype * at)
364 {
365 int i;
366 object *tmp;
367
368 for (i = 0; i < 16; i++)
369 if ((tmp = get_map_ob (emap, i, line)) &&
370 tmp->arch == at)
371 return (i);
372 return (-1);
373 }
374
375 static void EditInsertArch (Edit self, int x, int y, int i, archetype * at)
376 {
377 Window w;
378 mapstruct *emap;
379 object *op,*tmp;
380 int j = i;
381 XRectangle rect;
382
383 emap = self->emap;
384 w = XtWindow(self->w);
385 op = get_map_ob (emap, x, y);
386
387 /* check for duplicate object */
388 while (op && op->above)
389 op = op->above;
390 while (op && j-- > 0)
391 op = op->below;
392 if (op && op->arch == at)
393 return;
394
395 if (ob_blocked(&at->clone, self->emap, x, y) & P_OUT_OF_MAP) {
396 debug("Out of Map\n");
397 return;
398 }
399 op = object_create_arch (at);
400 if (op) {
401 MapInsertObjectZ(emap,op,x,y,i);
402 /*debug3 ("Inserting %s %d %d\n", op->name, op->x, op->y);*/
403
404 for (tmp = op; tmp; tmp = tmp->more) {
405 if (self->app->look.rect.x == tmp->x &&
406 self->app->look.rect.y == tmp->y)
407 AppUpdate(self->app);
408 }
409 rect.x = x;
410 rect.y = y;
411 rect.width = rect.height = 1;
412 CrEditRefresh(self->w,rect);
413 } else {
414 free_object (op);
415 debug0 ("Inserting failed\n");
416 }
417
418 return;
419 }
420
421 /*
422 * member: hmm
423 */
424 static int update_wall (Edit self, int x, int y, int add, int rem)
425 {
426 int i;
427 object *tmp, *op;
428 Window w;
429 mapstruct * emap;
430
431 emap = self->emap;
432 w = XtWindow(self->w);
433
434 if (out_of_map (emap, x, y) || !(op = get_map_ob (emap, x, y)))
435 return False;
436
437 while (op->above)
438 op = op->above;
439
440 /*
441 i = find_draw_arch (self->app->item.wall_map,
442 self->app->item.wall_set,
443 op->arch);
444 */
445 i = find_draw_arch (AppItemGetMap(self->app),
446 AppItemGetWall(self->app),
447 op->arch);
448 if (i >= 0) {
449 /* debug4 ("%d | %d & ~%d = %d\n", i, add, rem, (i | add) & ~rem); */
450 remove_ob (op);
451 free_object (op);
452 tmp = get_map_ob (AppItemGetMap(self->app), ((i | add) & ~rem),
453 AppItemGetWall(self->app));
454 if (tmp)
455 EditInsertArch (self, x, y, 0, tmp->arch);
456 return True;
457 }
458 return False;
459 }
460
461 /*
462 *
463 */
464 static void draw_add (Edit self, int x, int y)
465 {
466 int i, mask = 0;
467
468 if (!self->app->item.wall_map)
469 CnvDie(self->shell,"No Wall map");
470
471 if (MAP_WIDTH(self->app->item.wall_map) < 16)
472 CnvDie(self->shell,"Wall Map has wrong width\n");
473
474 for (i = 0; i < 4; i++)
475 if (update_wall (self, x + direction_x[i], y + direction_y[i],
476 (1 << i), 0))
477 mask |= (1 << (i ^ 2));
478
479 update_wall (self, x, y, mask, 0);
480 }
481
482 /*
483 *
484 */
485 static void draw_remove (Edit self, int x, int y)
486 {
487 if (!self->app->item.wall_map)
488 CnvDie(self->shell,"No Wall map");
489
490 if (MAP_WIDTH(self->app->item.wall_map) < 16)
491 CnvDie(self->shell,"Wall Map has wrong width\n");
492
493 update_wall (self, x, y + 1, 0, 1);
494 update_wall (self, x - 1, y, 0, 2);
495 update_wall (self, x, y - 1, 0, 4);
496 update_wall (self, x + 1, y, 0, 8);
497 }
498
499
500 /*
501 * member: resize and scroll mao of editor
502 * width : new width, if zero, no resize
503 * height: new height, if zero, no resize
504 * dx : amount to scroll, neg. to left, pos. to right
505 * dy : amount to scroll, neg, to up, pos. to down
506 */
507 void EditResizeScroll(Edit self,int width,int height,int dx,int dy)
508 {
509 mapstruct *tmp;
510 char buf[BUFSIZ];
511
512 if(!self || self->read_only) return;
513 if(self->type != ClipBoard &&
514 (width < MapMinWidth || width > MapMaxWidth ||
515 height < MapMinHeight || height > MapMaxHeight)) {
516 sprintf(buf,"Map cannot be smaller than %dx%d",
517 MapMinWidth,MapMinHeight);
518 CnvNotify(buf,"Continue",NULL);
519 return;
520 }
521
522
523 /*
524 * SelectUnset before EdFreeMap, since, it tries to
525 * do that, and there's no mapstuct at that moment
526 */
527 if(self->app->look.edit == self)
528 AppSelectUnset(self->app);
529
530 tmp = MapMoveScrollResize(self->emap, width, height, dx, dy);
531 self->emap = NULL; /* MapMoveScrollResize destroys the old map */
532 EdFreeMap(self);
533 self->emap = tmp;
534 EditModified(self);
535 EditUpdate(self);
536
537 CrEditRefresh(self->w,EditRectAll);
538
539 debug5("EditResizeScroll() %s %dx%d+%d+%d\n",
540 EditGetPath(self),width,height,dx,dy);
541 }
542
543 /**********************************************************************
544 * File-menu callbacks
545 **********************************************************************/
546
547 /*
548 * callback: save current map, ask name if not given
549 */
550 static void SaveCb (Widget w, XtPointer client, XtPointer call)
551 {
552 Edit self = (Edit) client;
553 EditSave(self);
554 }
555
556 /*
557 * callback: save map as name
558 */
559 static void SaveAsCb (Widget w, XtPointer client, XtPointer call)
560 {
561 Edit self = (Edit) client;
562 char path[PATH_MAX];
563
564 if(CnvPathSelect(self->app->path) == CnvPathCancel) return;
565 sprintf(path,"%s/%s",
566 self->app->path->current,
567 self->app->path->filename);
568 EdSaveMap(self,path);
569 }
570
571 /*
572 * callback: load current map again
573 */
574 static void ClearCb (Widget w, XtPointer client, XtPointer call)
575 {
576 Edit self = (Edit) client;
577 Map tmp;
578
579 debug1("ClearCb() %s\n",self->emap->path);
580 if(self->read_only) return;
581 tmp = get_empty_map(MAP_WIDTH(self->emap), MAP_HEIGHT(self->emap));
582 copy_map (self->emap, tmp);
583 EdFreeMap(self);
584 self->emap = tmp;
585 EditUnmodified(self);
586 EditUpdate(self);
587 }
588
589 /*
590 * callback: load new map
591 */
592 static void LoadCb (Widget w, XtPointer client, XtPointer call)
593 {
594 Edit self = (Edit) client;
595 char path[PATH_MAX+1],buf[BUFSIZ];
596
597 /*** save, if modified ***/
598 if (self->modified) {
599 switch (CnvNotify ("Map modified, save",
600 "OK","Cancel","No",NULL)) {
601 case 1:
602 EditSave(self);
603 break;
604 case 2:
605 return;
606 default:
607 break;
608 }
609 }
610 /*** load map ***/
611 if(CnvPathSelect(self->app->path) == CnvPathCancel) return;
612 sprintf(path,"%s/%s",self->app->path->current,self->app->path->filename);
613 /*** check out if map can load ***/
614 if (has_been_loaded (path)) {
615 sprintf(buf,"%s already in memory",path);
616 CnvNotify(buf,"Continue",NULL);
617 return;
618 }
619 Load(self,path);
620 }
621
622 /*
623 * callback: load current map again
624 */
625 static void ReloadCb (Widget w, XtPointer client, XtPointer call)
626 {
627 Edit self = (Edit) client;
628 debug1("ReloadCb() %s\n",self->emap->path);
629 Load(self,self->emap->path);
630 }
631
632 /*
633 * callback: enter to map in Look window
634 */
635 static void EnterCb (Widget w, XtPointer client, XtPointer call)
636 {
637 Edit self = (Edit) client;
638 object *obj = NULL, *tmp;
639 char buf[BUFSIZ];
640 char loadPath[PATH_MAX+1];
641 const char *path;
642 XRectangle rect;
643
644 /*** find object ***/
645 if(self->app->look.edit)
646 obj = MapGetObjectZ
647 (self->app->look.edit->emap,
648 self->app->look.rect.x,
649 self->app->look.rect.y,0);
650 if (!obj || !self->app->look.edit) {
651 CnvNotify("Select exit to enter","Continue",NULL);
652 return;
653 }
654 /*** foreach object in list, if exit load it ***/
655 tmp = obj;
656 do {
657 obj = tmp->head ? tmp->head : tmp;
658 if (obj->type == PLAYER_CHANGER || obj->type == TELEPORTER || obj->type==EXIT) {
659 /*** check out if exit leads ***/
660 if(!EXIT_PATH(obj)) {
661 CnvNotify("Exit leads nowhere",
662 "OK",NULL);
663 return;
664 }
665 path = EXIT_PATH(obj);
666 rect.x = EXIT_X(obj);
667 rect.y = EXIT_Y(obj);
668 /*** save current map if modified ***/
669 if (self->modified) {
670 switch (CnvNotify ("Map modified, save",
671 "Save","Cancel","Forget",NULL)) {
672 case 1:
673 EditSave(self);
674 break;
675 case 2:
676 return;
677 default:
678 break;
679 }
680 }
681 /*** load & update ***/
682 StrPathCd(strcpy(loadPath,EditGetPath(self)),path);
683 debug1("EnterCb() %s\n",loadPath);
684
685 /*** check out if map can load ***/
686 if (has_been_loaded (loadPath)) {
687 sprintf(buf,"%s already in memory",loadPath);
688 CnvNotify(buf,"Continue",NULL);
689 return;
690 }
691 if(!Load(self,loadPath)) return;
692 if (rect.x == 0 && rect.y == 0) {
693 rect.x = MAP_ENTER_X(self->emap);
694 rect.y = MAP_ENTER_Y(self->emap);
695 }
696 rect.width = rect.height = 0;
697 AppSelectSet(self->app, self, rect);
698 CrEditSelect(self->w, rect);
699 return;
700 }
701 tmp = tmp->below;
702 } while(tmp);
703 CnvNotify("That's not exit","Continue",NULL);
704 }
705
706 /*
707 * callback: reset this map from crossfire
708 */
709 static void ResetCb (Widget w, XtPointer client, XtPointer call)
710 {
711 Edit self = (Edit) client;
712 char cmd[ARG_MAX+1],buf[MAX_INPUT+1];
713
714 debug0("ResetCb()\n");
715 if(!self->app->res.cmdReset) {
716 CnvNotify("no defined *cmdReset:","Continue",NULL);
717 return;
718 }
719 sprintf(cmd,self->app->res.cmdReset,EditGetPath(self));
720 if(system(cmd)) {
721 sprintf(buf,"Command failed \"%s\"",cmd);
722 CnvNotify(buf,"Continue",NULL);
723 }
724 }
725
726 /*
727 * callback: quit map editor
728 */
729 static void CloseCb (Widget w, XtPointer client, XtPointer call)
730 {
731 char buf[BUFSIZ];
732 Edit self = (Edit) client;
733 if (EditIsModified(self)) {
734 sprintf (buf, "Quitting, save %s ?", self->emap->path);
735 switch (CnvNotify (buf,"Save","Cancel", "Forget",NULL)) {
736 case 1: /* ok */
737 EditSave(self);
738 break;
739 case 2:
740 return;
741 default:
742 EditUnmodified(self); /* to fool EditDestroy */
743 break;
744 }
745 }
746 EditDestroy(self);
747 }
748
749 static CnvMenuRec fileMenu[] = {
750 {"save", SaveCb},
751 {"saveAs", SaveAsCb},
752 {"----", NULL},
753 {"load", LoadCb},
754 {"reload", ReloadCb},
755 {"----", NULL},
756 {"clear", ClearCb},
757 {"reset", ResetCb},
758 {"----", NULL},
759 {"enter", EnterCb},
760 {"----", NULL},
761 {"close", CloseCb},
762 {"", NULL },
763 };
764
765 /**********************************************************************
766 * Auto Creating stuff
767 **********************************************************************/
768
769 static char * MapMessageCreate (App self) {
770 char buf[MAX_BUF];
771 time_t t = time(NULL);
772 sprintf (buf, "Creator: %s\nEmail: %s\nDate: %s",
773 self->res.creator,self->res.email, ctime (&t));
774 return strdup_local (buf);
775 }
776
777 #if 0
778 static char * MapNameCreate (String path)
779 {
780 char name[PATH_MAX+1];
781 int i,j;
782
783 strncpy(name,path,PATH_MAX);
784 name[PATH_MAX] = '\0';
785 for(i = strlen(name)-1; i && name[i] != '/';i--);
786 i++;
787 for(j = i; name[j] && name[j] != '.';j++);
788 if(name[j] == '.') name[j] = 0;
789 return strdup_local (&name[i]);
790 }
791 #endif
792 /**********************************************************************
793 * Options - menu
794 **********************************************************************/
795
796
797 #if 0
798 /*
799 * callback: set new path of map
800 */
801 static void SetPathCb (Widget w, XtPointer client, XtPointer call)
802 {
803 char path[CnvPromptMax+1];
804 Edit self = (Edit) client;
805
806 switch (CnvPrompt ("Set path", self->emap->path, path,
807 "OK","Cancel",NULL)) {
808 case 1:
809 EditSetPath(self,path);
810 default:
811 break;
812 }
813 }
814
815 /*
816 * callback: resize and scroll map
817 */
818 static void ResizeCb (Widget w, XtPointer client, XtPointer call)
819 {
820 Edit self = (Edit) client;
821 char buf[1025], path[CnvPromptMax+1];
822 unsigned x, y;
823 int sx = 0, sy = 0, res;
824
825 if (self->read_only)
826 return;
827
828 sprintf (buf, "%dx%d+0+0",
829 MAP_WIDTH(self->emap), MAP_HEIGHT(self->emap));
830 switch (CnvPrompt ("ResizeScroll",
831 buf, path,"OK",NULL)) {
832 case 1:
833 res = XParseGeometry (path, &sx, &sy, &x, &y);
834
835 if (!(res & WidthValue))
836 x = MAP_WIDTH(self->emap);
837 if (!(res & HeightValue))
838 y = MAP_HEIGHT(self->emap);
839
840 if (!(res & XValue))
841 sx = 0;
842 if (!(res & YValue))
843 sy = 0;
844
845 if (x < 3 || y < 3 || x > 100 || y > 100) {
846 CnvNotify ("Illegal size","OK",NULL);
847 return;
848 }
849 EditResizeScroll (self, x,y,sx, sy);
850 break;
851 default:
852 return;
853 }
854 }
855
856 /*
857 * callback: set start point
858 */
859 static void StartCb (Widget w, XtPointer client, XtPointer call)
860 {
861 Edit self = (Edit) client;
862 char buf[BUFSIZ], reply[CnvPromptMax+1];
863 int x, y, flags;
864 int width, height;
865
866 sprintf (buf, "%dx%d",
867 MAP_ENTER_X(self->emap),
868 MAP_ENTER_Y(self->emap));
869 switch (CnvPrompt ("Start of map", buf,reply,
870 "OK","Cancel",NULL)) {
871 case 1: /* ok */
872 flags = XParseGeometry (reply, &x, &y, &width, &height);
873 if ((flags != (WidthValue | HeightValue))) {
874 CnvNotify ( "Illegal value","Ok",NULL);
875 return;
876 }
877 if (out_of_map(self->emap,width,height)) {
878 sprintf (buf, "Point %dx%d out of map", width, height);
879 CnvNotify (buf,"OK",NULL);
880 return;
881 } else {
882 MAP_ENTER_X(self->emap) = width;
883 MAP_ENTER_Y(self->emap) = height;
884 EditModified(self);
885 }
886 EditUpdate(self);
887 return;
888 default:
889 return;
890 }
891 }
892 #endif
893
894
895
896 /*
897 * Edit attributes of the Map
898 */
899 static void AttributesCb (Widget w, XtPointer client, XtPointer call)
900 {
901 Edit self = (Edit)client;
902
903 debug0("Edit::AttributesCb()\n");
904
905 if (self->mapattr == NULL) {
906 self->mapattr = MapAttrCreate(self->emap, self->app, self);
907 } else {
908 MapAttrDestroy(self->mapattr);
909 }
910 }
911
912 /*
913 * callback: refresh map
914 */
915 static void RefreshCb (Widget w, XtPointer client, XtPointer call)
916 {
917 Edit self = (Edit)client;
918 CrEditRefresh(self->w,EditRectAll);
919 EditUpdate(self);
920 }
921
922 #if 0
923 /*
924 * callback: toggle write accces
925 */
926 static void ToggleReadCb (Widget w, XtPointer client, XtPointer call)
927 {
928 Edit self = (Edit)client;
929
930 if(self->type == Pick ||
931 self->type == Wall) return;
932 ((Edit) client)->read_only++;
933 EditUpdate(self);
934 debug ("CbEditToggleRead()\n");
935 }
936 #endif
937
938 /*
939 * callback: Toggle overwrite mode
940 */
941 static void ToggleOverCb (Widget w, XtPointer client, XtPointer call)
942 {
943 Edit self = (Edit)client;
944
945 if(self->type == Pick ||
946 self->type == Wall) return;
947 ((Edit) client)->overwrite++;
948 EditUpdate(self);
949 debug ("CbEditToggleOver()\n");
950 }
951
952 /*
953 * callback: Toggle auto choose
954 */
955 static void ToggleAutoCb (Widget w, XtPointer client, XtPointer call)
956 {
957 Edit self = (Edit)client;
958
959 if(self->type == Pick ||
960 self->type == Wall) return;
961 ((Edit) client)->auto_choose++;
962 EditUpdate(self);
963 debug ("CbEditToggleAuto()\n");
964 }
965
966 /*
967 * callback: Toggle showing weak walls
968 */
969 static void ToggleWeakCb (Widget w, XtPointer client, XtPointer call)
970 {
971 Edit self = (Edit)client;
972 Cardinal weak_walls;
973
974 XtVaGetValues(self->w, XtNshow_weak_walls, &weak_walls, NULL);
975 XtVaSetValues(self->w, XtNshow_weak_walls, !weak_walls, NULL);
976
977 EditUpdate(self);
978 debug("CbEditToggleWeak()\n");
979 }
980
981 /*
982 * callback: Adjust stacking size
983 */
984 static void SetStackingCb(Widget w, XtPointer client, XtPointer call)
985 {
986 char buf[MAX_BUF];
987 char retbuf[CnvPromptMax+1];
988 Edit self = (Edit)client;
989 Cardinal stacking;
990 int ret;
991
992 XtVaGetValues(self->w, XtNstacking, &stacking, NULL);
993
994 snprintf(buf, sizeof(buf), "%d", stacking);
995
996 ret = CnvPrompt("Set stacking (0 = off)", buf, retbuf, "OK", "Cancel", NULL);
997
998 if (ret != 1)
999 return;
1000
1001 stacking = atoi(retbuf);
1002
1003 debug2("SetStackingCb %s %d\n", retbuf, stacking);
1004
1005 /* TODO How did they come up with this magic number? */
1006 if (stacking > 48) {
1007 CnvNotify("Illegal space", "Ok", NULL);
1008 return;
1009 }
1010 XtVaSetValues(self->w, XtNstacking, stacking, NULL);
1011
1012 EditUpdate(self);
1013 }
1014
1015 static Widget OptionMenu(String name,Edit self,Widget parent)
1016 {
1017 Widget shell,refresh, use;
1018
1019 shell = XtVaCreatePopupShell
1020 (name, simpleMenuWidgetClass, parent, NULL);
1021
1022 use = XtVaCreateManagedWidget
1023 ("attributes",smeBSBObjectClass, shell,
1024 NULL);
1025 XtAddCallback(use,XtNcallback,AttributesCb,(XtPointer)self);
1026
1027 /*** refresh ***/
1028 XtVaCreateManagedWidget ("line", smeLineObjectClass, shell, NULL);
1029 refresh = XtVaCreateManagedWidget
1030 ("refresh",smeBSBObjectClass,shell,
1031 NULL);
1032 XtAddCallback
1033 (refresh,XtNcallback,RefreshCb,(XtPointer)self);
1034 /*** toggles ***/
1035 XtVaCreateManagedWidget ("line", smeLineObjectClass, shell, NULL);
1036 self->iw.overwrite = XtVaCreateManagedWidget
1037 ("overWrite", smeBSBObjectClass, shell, NULL);
1038 XtAddCallback (self->iw.overwrite,
1039 XtNcallback,ToggleOverCb, (XtPointer) self);
1040 self->iw.auto_choose = XtVaCreateManagedWidget
1041 ("autoChoose", smeBSBObjectClass, shell, NULL);
1042 XtAddCallback (self->iw.auto_choose,
1043 XtNcallback,ToggleAutoCb, (XtPointer) self);
1044 self->iw.weak_walls = XtVaCreateManagedWidget
1045 ("weakWalls", smeBSBObjectClass, shell, NULL);
1046 XtAddCallback (self->iw.weak_walls,
1047 XtNcallback,ToggleWeakCb, (XtPointer) self);
1048 self->iw.stacking = XtVaCreateManagedWidget
1049 ("sparse", smeBSBObjectClass, shell, NULL);
1050 XtAddCallback (self->iw.stacking,
1051 XtNcallback,SetStackingCb, (XtPointer) self);
1052
1053 return shell;
1054 }
1055
1056 /**********************************************************************
1057 * edit routines - CrEdit callbacks
1058 * insert
1059 * select
1060 * delete
1061 * align
1062 * feed
1063 **********************************************************************/
1064
1065 static void InsertCb(Widget w,XtPointer clientData,XtPointer callData)
1066 {
1067 Edit self = (Edit)clientData;
1068 CrEditCall call = (CrEditCall)callData;
1069 AppSelectUnset(self->app);
1070 EditInsert(self,call->rect.x,call->rect.y,call->z);
1071 }
1072
1073 static void SelectCb(Widget w,XtPointer clientData,XtPointer callData)
1074 {
1075 Edit self = (Edit)clientData;
1076 CrEditCall call = (CrEditCall)callData;
1077 if(self->auto_choose) {
1078 if(self->type == Wall && call->rect.x == 0) {
1079 AppItemSet(self->app,self,NULL,call->rect.y);
1080 } else {
1081 object *obj;
1082 obj = MapGetRealObject(self->emap,call->rect.x,call->rect.y,0);
1083 AppItemSet(self->app,self,obj,AppItemNoWall);
1084 }
1085 } else {
1086 AppSelectSet(self->app,self,call->rect);
1087 }
1088 }
1089
1090 static void PropsCb(Widget w,XtPointer clientData,XtPointer callData)
1091 {
1092 Edit self = (Edit)clientData;
1093 CrEditCall call = (CrEditCall)callData;
1094 debug0 ("PropsCb()\n");
1095 if(!self->auto_choose) {
1096 object *ob = MapGetRealObject
1097 (self->emap, call->rect.x, call->rect.y, call->z);
1098 if(!self->app->attr) {
1099 self->app->attr = AttrCreate
1100 ("attr", self->app, ob, AttrDescription, GetType(ob), self);
1101 } else {
1102 AttrChange (self->app->attr,ob, GetType(ob), self);
1103 }
1104 }
1105 return;
1106 }
1107
1108 static void DeleteCb(Widget w,XtPointer clientData,XtPointer callData)
1109 {
1110 Edit self = (Edit)clientData;
1111 CrEditCall call = (CrEditCall)callData;
1112 AppSelectUnset(self->app);
1113 EditDelete(self,call->rect.x,call->rect.y,call->z);
1114 }
1115
1116 static void AlignCb(Widget w,XtPointer clientData,XtPointer callData)
1117 {
1118 Edit self = (Edit)clientData;
1119 CrEditCall call = (CrEditCall)callData;
1120 debug4("Edit::AlignCb() by %dx%d+%d+%d\n",call->rect.width,
1121 call->rect.height,call->rect.x,call->rect.y);
1122 EditResizeScroll(self,call->rect.width,
1123 call->rect.height,call->rect.x,call->rect.y);
1124 }
1125
1126 static void FeedCb(Widget w,XtPointer clientData,XtPointer callData)
1127 {
1128 Edit self = (Edit)clientData;
1129 CrEditCall call = (CrEditCall)callData;
1130 char buf[BUFSIZ],*str;
1131
1132 debug("FeedCb()\n");
1133 /*** path, modified for game, not clean thing ***/
1134 str = buf;
1135 if (!self->app->attr || !self->app->attr->op)
1136 return;
1137 if(!strcmp(self->app->attr->op->map->path,call->map->path)) {
1138 StrBasename(buf,self->app->attr->op->map->path);
1139 } else {
1140 strcpy(buf,self->app->attr->op->map->path);
1141 StrPathGenCd(buf,call->map->path);
1142 if(strlen(buf) > 2) str = &buf[2];
1143 }
1144 XtVaSetValues (self->app->attr->tags[I_Path].value,
1145 XtNstring, str,
1146 NULL);
1147 /*** x ***/
1148 sprintf (buf, "%d", call->rect.x);
1149 XtVaSetValues (self->app->attr->tags[I_X].value,
1150 XtNstring, buf,
1151 NULL);
1152 /*** y ***/
1153 sprintf (buf, "%d", call->rect.y);
1154 XtVaSetValues (self->app->attr->tags[I_Y].value,
1155 XtNstring, buf,
1156 NULL);
1157 }
1158
1159 /**********************************************************************
1160 * layout
1161 **********************************************************************/
1162
1163 static void Layout(Edit self,Widget parent,Cardinal stacking)
1164 {
1165 Widget vbox, hbox,use;
1166 Widget editMenu, optionMenu;
1167
1168 /*** shell ***/
1169 self->shell = XtVaCreatePopupShell
1170 ("edit",topLevelShellWidgetClass, self->app->shell,
1171 XtNwidth,
1172 (MAP_WIDTH(self->emap) > self->app->res.mapWidth ?
1173 self->app->res.mapWidth * FontSize :
1174 MAP_WIDTH(self->emap) * FontSize ) + 16,/* kludge */
1175 XtNheight,
1176 (MAP_HEIGHT(self->emap) > self->app->res.mapHeight ?
1177 self->app->res.mapHeight * FontSize :
1178 MAP_HEIGHT(self->emap) * FontSize ) + 46,/* kludge */
1179 XtNiconPixmap,bitmaps.edit,
1180 NULL);
1181 vbox = XtVaCreateManagedWidget ("vbox", panedWidgetClass,
1182 self->shell, NULL);
1183
1184 /*** menu bar ***/
1185 hbox = XtVaCreateManagedWidget ("hbox", boxWidgetClass,
1186 vbox, NULL);
1187
1188 if(self->type != ClipBoard) {
1189 use = XtVaCreateManagedWidget
1190 ("mapFileButton", menuButtonWidgetClass, hbox,
1191 XtNmenuName,"fileMenu",
1192 NULL);
1193 CnvMenu("fileMenu",use,fileMenu,(XtPointer)self);
1194
1195 editMenu = XtVaCreateManagedWidget
1196 ("mapEditButton", menuButtonWidgetClass, hbox,
1197 XtNmenuName,"mapEdit",
1198 NULL);
1199
1200 optionMenu = XtVaCreateManagedWidget
1201 ("mapOptionButton", menuButtonWidgetClass, hbox,
1202 XtNmenuName,"optionMenu",
1203 NULL);
1204 OptionMenu("optionMenu",self,optionMenu);
1205 }
1206
1207
1208 /*** editor-widget ***/
1209 self->view = XtVaCreateManagedWidget
1210 ("veivi",viewportWidgetClass, vbox,
1211 XtNallowResize,True,
1212 XtNforceBars,True,
1213 XtNallowHoriz,True,
1214 XtNallowVert,True,
1215 NULL);
1216 self->w = XtVaCreateManagedWidget
1217 ("cross",crEditWidgetClass,self->view,
1218 XtNresizable,True,
1219 XtNmap,self->emap,
1220 XtNstacking,stacking,
1221 XtNselectArea,True,
1222 NULL);
1223 XtAddCallback(self->w,XtNinsertCallback,InsertCb,(XtPointer)self);
1224 XtAddCallback(self->w,XtNselectCallback,SelectCb,(XtPointer)self);
1225 XtAddCallback(self->w,XtNpropsCallback,PropsCb,(XtPointer)self);
1226 XtAddCallback(self->w,XtNdeleteCallback,DeleteCb,(XtPointer)self);
1227 XtAddCallback(self->w,XtNalignCallback,AlignCb,(XtPointer)self);
1228 XtAddCallback(self->w,XtNfeedCallback,FeedCb,(XtPointer)self);
1229 }
1230
1231 /**********************************************************************
1232 * public
1233 **********************************************************************/
1234
1235 /*
1236 * create editing window for map for crossfire-abs-path file-name
1237 */
1238 Edit EditCreate(App app,EditType type,String path)
1239 {
1240 Edit self;
1241
1242 /*** initialising ***/
1243 self = (Edit)XtMalloc(sizeof(struct _Edit));
1244 self->app = app;
1245 self->shell = NULL;
1246 self->w = NULL;
1247 self->view = NULL;
1248 self->next = NULL;
1249 self->emap = NULL;
1250 self->type = type;
1251 self->overwrite = 0;
1252 self->mapattr = NULL;
1253 self->modified = False;
1254
1255 /*** load or create map ***/
1256 if (type == ClipBoard) {
1257 if (!self->emap)
1258 self->emap = get_empty_map(MapMinWidth,MapMinHeight);
1259 strcpy (self->emap->path, "/ClipBoard");
1260
1261 if (!self->emap->msg) {
1262 self->emap->msg = MapMessageCreate (app);
1263 }
1264 } else if(path && *path != '\0') {
1265 if (!Load(self,path)) return 0;
1266 } else {
1267 self->emap = get_empty_map(16,16);
1268 strcpy (self->emap->path, "/Noname");
1269
1270 if (!self->emap->msg) {
1271 self->emap->msg = MapMessageCreate (app);
1272 }
1273 }
1274
1275 if (!self->emap)
1276 return 0;
1277
1278 /*** creating ***/
1279 Layout(self,self->app->shell,0);
1280 EditUnmodified(self);
1281
1282 /*** type specific ***/
1283 switch (self->type) {
1284 case Regular:
1285 self->read_only = 0;
1286 self->auto_choose = 0;
1287 break;
1288 case Pick:
1289 case Wall:
1290 self->read_only = 1;
1291 self->auto_choose = 1;
1292 XtVaSetValues(self->w,
1293 XtNstacking,1,
1294 XtNselectArea,False,
1295 NULL);
1296 break;
1297 case ClipBoard:
1298 self->read_only = 0;
1299 self->auto_choose = 0;
1300 break;
1301 default:
1302 fprintf(stderr,"unknown MapType");
1303 exit(EXIT_FAILURE);
1304 }
1305 EditUpdate(self);
1306 /*** show-map-editor ***/
1307 if(type != ClipBoard)
1308 XtPopup (self->shell, XtGrabNone);
1309
1310 return self;
1311 }
1312
1313 /*
1314 * member: destroy edit
1315 */
1316 void EditDestroy(Edit self)
1317 {
1318 char buf[BUFSIZ];
1319
1320 if(!self) return;
1321 debug1 ("EditDestroy() %s\n",EditGetPath(self));
1322
1323 /*** reply to save if designed ***/
1324 if (EditIsModified(self)) {
1325 sprintf (buf, "Quitting, save %s ?", self->emap->path);
1326 switch (CnvNotify (buf,"Save","Forget",NULL)) {
1327 case 1: /* ok */
1328 EditSave(self);
1329 break;
1330 default:
1331 break;
1332 }
1333 }
1334 /*** outer coonections ***/
1335 if (self->mapattr) {
1336 MapAttrDestroy(self->mapattr);
1337 }
1338 if (self->app->attr && self->app->attr->op &&
1339 self->app->attr->op->map == self->emap) {
1340 AttrChange(self->app->attr, NULL, 0, (Edit)0);
1341 }
1342 if (AppItemGetEdit(self->app) == self) {
1343 AppItemSet (self->app, NULL,NULL,0);
1344 }
1345 AppUpdate(self->app);
1346 AppEditDeattach(self->app,self);
1347 EdFreeMap (self);
1348
1349 /*** inner remove ***/
1350 XtDestroyWidget (self->shell);
1351 XtFree((char*)self);
1352 }
1353
1354 /*
1355 * member: update Edit values
1356 */
1357 void EditUpdate(Edit self)
1358 {
1359 char buf[BUFSIZ];
1360
1361 if(!(self && self->shell && self->emap)) return;
1362
1363 /*** map title ***/
1364 snprintf(buf, sizeof(buf),
1365 "Edit %s %s",
1366 self->emap->path,
1367 self->modified ? "*" : "");
1368 XtVaSetValues(self->shell,
1369 XtNtitle,buf,
1370 XtNiconName,buf,
1371 NULL);
1372
1373 /*** info ***/
1374 if (self->mapattr != NULL)
1375 MapAttrChange(self->mapattr, self->emap);
1376
1377 /*** toggles ***/
1378 if(self->type != ClipBoard) {
1379 Cardinal weak_walls;
1380 Cardinal stacking;
1381
1382 #if 0
1383 XtVaSetValues
1384 (self->iw.read_only,
1385 XtNleftBitmap, self->read_only ? bitmaps.mark : None, NULL);
1386 #endif
1387 XtVaSetValues
1388 (self->iw.overwrite,
1389 XtNleftBitmap, self->overwrite ? bitmaps.mark : None, NULL);
1390 XtVaSetValues
1391 (self->iw.auto_choose,
1392 XtNleftBitmap, self->auto_choose ? bitmaps.mark : None,
1393 NULL);
1394
1395 XtVaGetValues(self->w, XtNshow_weak_walls, &weak_walls, NULL);
1396 XtVaSetValues
1397 (self->iw.weak_walls,
1398 XtNleftBitmap, weak_walls ? bitmaps.mark : None,
1399 NULL);
1400
1401 XtVaGetValues(self->w, XtNstacking, &stacking, NULL);
1402 XtVaSetValues
1403 (self->iw.stacking,
1404 XtNleftBitmap, (stacking != 0) ? bitmaps.mark : None,
1405 NULL);
1406 }
1407 /*** map area ***/
1408 if(self->emap && self->w)
1409 XtVaSetValues(self->w,XtNmap,self->emap,NULL);
1410
1411 if (self->app->look.edit == self)
1412 CrEditRefresh (self->w, self->app->look.rect);
1413 }
1414
1415 /*
1416 * member : save map from memory to file, there should be no other
1417 * map saving routine than self
1418 * self : pointer to current Edit structure
1419 * name : filename of map, relative ( level number ) ???
1420 * return : True, map is saved
1421 */
1422 static Boolean EdSaveMap (Edit self, char *name)
1423 {
1424 mapstruct *m = self->emap;
1425 char buf[BUFSIZ];
1426
1427 debug0 ("Saving Map by name\n");
1428
1429 strcpy (m->path, name);
1430
1431 /*** updating map name here ***/
1432 EditSetPath(self,name);
1433 if(new_save_map (m, 1)) {
1434 sprintf(buf,"Error in saving map %s",EditGetPath(self));
1435 CnvNotify ("Error in saving map !","OK",NULL);
1436 return False;
1437 }
1438 EditUnmodified(self);
1439 return True;
1440 }
1441
1442 /*
1443 * member: save contents of map to file
1444 * return: status
1445 */
1446 EditReturn EditSave(Edit self)
1447 {
1448 char path[PATH_MAX+1];
1449
1450 if (self->read_only) {
1451 CnvNotify ( "Map is read-only!","OK",NULL);
1452 return EditOk;
1453 }
1454 if (!self->modified) {
1455 CnvNotify ( "No changes to save", "OK",NULL);
1456 return EditOk;
1457 }
1458 /*** if empty path, ask it ***/
1459 if (!*self->emap->path) {
1460 if(CnvPathSelect(self->app->path) == CnvPathCancel) return EditOk;
1461 sprintf(path,"%s/%s",self->app->path->current,
1462 self->app->path->filename);
1463 strcpy (self->emap->path, path);
1464 }
1465 if(!EdSaveMap(self,self->emap->path)) return EditOk;
1466 EditUpdate(self);
1467 return EditOk;
1468 }
1469
1470 /*
1471 * member: load map from file
1472 * self : pointer to current Edit structure
1473 * return : status
1474 */
1475 EditReturn EditLoad(Edit self)
1476 {
1477 if(Load(self, self->emap->path))
1478 return EditOk;
1479 else
1480 return EditError;
1481 }
1482
1483 /*
1484 * member: make fill
1485 * x,y : point to start fill
1486 */
1487 void EditPerformFill(Edit self, int x, int y)
1488 {
1489 int dx, dy;
1490 if (!self->emap) return;
1491
1492 for (dx=x; dx< x+self->app->look.rect.width; dx++) {
1493 for (dy=y; dy < y+self->app->look.rect.height; dy++) {
1494 if (!OUT_OF_REAL_MAP (self->emap, dx, dy))
1495 EditInsert(self, dx, dy, 0);
1496 }
1497 }
1498 }
1499
1500 /*
1501 * member: make fill
1502 * x,y : point to start fill
1503 */
1504 void EditPerformFillBelow(Edit self, int x, int y)
1505 {
1506 int dx, dy;
1507 if (!self->emap) return;
1508
1509 for (dx=x; dx< x+self->app->look.rect.width; dx++) {
1510 for (dy=y; dy < y+self->app->look.rect.height; dy++) {
1511 if (!OUT_OF_REAL_MAP (self->emap, dx, dy))
1512 EditInsert(self, dx, dy, -1);
1513 }
1514 }
1515 }
1516
1517 /*
1518 * member: fill
1519 * x,y : point to start fill
1520 */
1521 void EditFillRectangle(Edit self, XRectangle rec)
1522 {
1523 int x,y;
1524
1525 if (!self->emap || self->read_only)
1526 return;
1527
1528 for(x = 0; x < rec.width; x++)
1529 for(y = 0; y < rec.height; y++)
1530 EditInsert (self, x + rec.x, y + rec.y, 0);
1531
1532 return;
1533 }
1534
1535
1536 /*
1537 * member: wipe
1538 * x,y : point to start fill
1539 */
1540 void EditWipeRectangle(Edit self, XRectangle rec)
1541 {
1542 int x,y;
1543
1544 if (!self || !self->emap || self->read_only)
1545 return;
1546
1547 for(x=0; x < rec.width; x++)
1548 for(y=0; y < rec.height; y++)
1549 while(get_map_ob(self->emap,x + rec.x, y + rec.y))
1550 EditObjectDelete (self, x + rec.x, y + rec.y, 0);
1551 CrEditRefresh (self->w, rec);
1552 return;
1553 }
1554
1555 /*
1556 * member: shave
1557 * x,y : point to start fill
1558 */
1559 void EditShaveRectangle(Edit self, XRectangle rec)
1560 {
1561 int x,y;
1562
1563 if (!self || !self->emap || self->read_only)
1564 return;
1565
1566 for(x=0; x < rec.width; x++)
1567 for(y=0; y < rec.height; y++)
1568 EditObjectDelete (self, x + rec.x, y + rec.y, 0);
1569
1570 CrEditRefresh (self->w, rec);
1571 return;
1572 }
1573
1574 void EditShaveRectangleBelow(Edit self, XRectangle rec)
1575 {
1576 int x,y;
1577
1578 if (!self || !self->emap || self->read_only)
1579 return;
1580
1581 for(x=0; x < rec.width; x++)
1582 for(y=0; y < rec.height; y++)
1583 EditObjectDelete (self, x + rec.x, y + rec.y, -1);
1584
1585 CrEditRefresh (self->w, rec);
1586 return;
1587 }
1588
1589 /*
1590 * member: copy rectangle area (x,y,width,height) from src to
1591 * self to point (x,y)
1592 */
1593 void EditCopyRectangle(Edit self,Edit src,XRectangle rect,int sx,int sy)
1594 {
1595 int x, y, z;
1596 object *obj,*tmp;
1597
1598 if(!self ||
1599 !src ||
1600 self->read_only)
1601 return;
1602
1603 if (rect.width > MAP_WIDTH(self->emap) - sx)
1604 rect.width = MAP_WIDTH(self->emap) - sx;
1605 if (rect.width > MAP_WIDTH(src->emap))
1606 rect.width = MAP_WIDTH(src->emap);
1607
1608 if (rect.height > MAP_HEIGHT(self->emap) - sy)
1609 rect.height = MAP_HEIGHT(self->emap);
1610 if (rect.height > MAP_HEIGHT(src->emap))
1611 rect.height = MAP_HEIGHT(src->emap);
1612
1613 debug2("EditCopyRectangle() %s -> %s\n",EditGetPath(src),
1614 EditGetPath(self));
1615 if(self->overwrite)
1616 for(x=0; x < rect.width; x++)
1617 for(y=0; y < rect.height; y++)
1618 EditDeletePoint(self,(sx+x),(sy+y));
1619
1620 for(x=0; x < rect.width; x++)
1621 for(y=0; y < rect.height; y++) {
1622 obj = MapGetObjectZ(src->emap,(rect.x + x),(rect.y + y),0);
1623 for(tmp = obj,z=0; tmp; tmp = tmp->below,z++) {
1624 if(tmp->head) {continue;}
1625 /*EditObjectInsert(self,tmp,(sx+x),(sy+y),z);*/
1626 EditCloneInsert(self,tmp,(sx+x),(sy+y),z);
1627 }
1628 }
1629 rect.x = sx;
1630 rect.y = sy;
1631 CrEditRefresh (self->w, rect);
1632 EditModified(self);
1633 }
1634
1635 /*
1636 *
1637 */
1638 void EditDeletePoint(Edit self,int x,int y)
1639 {
1640 while(get_map_ob(self->emap,x,y))
1641 EditDelete(self,x,y,0);
1642 }
1643
1644 /*
1645 * Member: set Edit to modified state
1646 */
1647 void EditModified(Edit self)
1648 {
1649 if (self && !self->modified) {
1650 self->modified = True;
1651 EditUpdate(self);
1652 }
1653 }
1654
1655 /*
1656 * member: set Edit to modified state
1657 */
1658 void EditUnmodified(Edit self)
1659 {
1660 if (self && self->modified) {
1661 self->modified = False;
1662 EditUpdate(self);
1663 }
1664 }
1665
1666 static void EditObjectCalc(Edit self,object *obj,XRectangle *rect)
1667 {
1668 object *tmp;
1669
1670 rect->x = 10000;
1671 rect->y = 10000;
1672 rect->width = 0;
1673 rect->height = 0;
1674 for (tmp = obj; tmp; tmp = tmp->more) {
1675 if (self->app->look.rect.x == tmp->x &&
1676 self->app->look.rect.y == tmp->y)
1677 AppUpdate(self->app);
1678 if(tmp->x < rect->x) rect->x = tmp->x;
1679 if(tmp->y < rect->y) rect->y = tmp->y;
1680 if(tmp->x - rect->x + 1> rect->width)
1681 rect->width = tmp->x - rect->x + 1;
1682 if(tmp->y - rect->y + 1> rect->height)
1683 rect->height = tmp->y - rect->y + 1;
1684 }
1685 }
1686
1687 /*
1688 * member: delete object from map of editor
1689 * x,y,z : delete object from self point
1690 * return: True, if object deleted
1691 */
1692 Boolean EditDelete (Edit self, int x, int y, int z)
1693 {
1694 object *obj;
1695 XRectangle rect;
1696
1697 obj = MapGetObjectZ(self->emap, x, y, z);
1698 if (self->read_only || z < 0 || !obj)
1699 return False;
1700 if (obj->head)
1701 obj = obj->head;
1702
1703 EditObjectCalc (self,obj,&rect);
1704 if (!EditObjectDelete (self, x, y, z))
1705 return False;
1706 CrEditRefresh (self->w,rect);
1707
1708 if (self->app->item.wall_map)
1709 draw_remove (self, x, y);
1710
1711 EditModified(self);
1712 return True;
1713 }
1714
1715 /*
1716 * member: delete object from map of editor
1717 * x,y,z : delete object from self point
1718 * return: True, if object deleted
1719 */
1720 Boolean EditObjectDelete (Edit self, int x, int y, int z)
1721 {
1722 object *obj;
1723
1724 obj = MapGetObjectZ(self->emap, x, y, z);
1725 if (self->read_only || !obj)
1726 return False;
1727
1728 if (obj->head)
1729 obj = obj->head;
1730
1731 if (z < 0) {
1732 if (obj->below)
1733 obj = obj->below;
1734 else
1735 return False;
1736 }
1737 /* This is just plain wrong
1738 ** -- deletes object above the selected object!
1739 ** if (z > 0)
1740 ** obj = obj->above;
1741 */
1742 if (self->app->attr && self->app->attr->op == obj)
1743 AttrChange(self->app->attr, NULL, 0, 0);
1744 if (AppItemGetEdit(self->app) == self)
1745 AppItemSet (self->app, NULL,NULL,0);
1746
1747 debug4("EditDelete() %s in %dx%dx%d\n",obj->name,x, y, z);
1748
1749 remove_ob (obj);
1750 free_object (obj);
1751 /* remove_ob should do this for us */
1752 /* But it doesnt - ds */
1753 SET_MAP_FLAGS(self->emap, x, y, P_NEED_UPDATE);
1754 update_position (self->emap, x, y);
1755 return True;
1756 }
1757
1758 /*
1759 * member: insert object to point of map of editor
1760 * object:
1761 * x,y,z : point to insert
1762 * return: True, if obejct inserted
1763 */
1764 object *EditCloneInsert (Edit self,object *obj,int x, int y, int z)
1765 {
1766 object *op;
1767 int button_value;
1768
1769 /*** create & insert ***/
1770 if ((op = object_create_clone(obj))) {
1771 /*** object do not fit ***/
1772 if(!MapObjectOut(self->emap,op,x,y)) {
1773 MapInsertObjectZ (self->emap, op, x, y, z);
1774 if (QUERY_FLAG(obj, FLAG_IS_LINKED) &&
1775 (button_value = get_button_value(obj))) {
1776 add_button_link(op, self->emap, button_value);
1777 }
1778 return op;
1779 }
1780 }
1781 debug0 ("Inserting failed\n");
1782 return NULL;
1783 }
1784
1785 /*
1786 * member: insert object to point of map of editor
1787 * x,y,z : point to insert
1788 * return: True, if obejct inserted
1789 */
1790 Boolean EditInsert (Edit self,int x, int y, int z)
1791 {
1792 int zi = z;
1793 object *op,*tmp;
1794 XRectangle rect;
1795
1796 /*** ***/
1797 if (!self ||
1798 self->read_only ||
1799 !self->emap ||
1800 out_of_map (self->emap, x, y))
1801 return False;
1802
1803 /*** something to insert ***/
1804 if (!AppItemGetObject(self->app) && !AppItemGetMap(self->app)) {
1805 CnvNotify ( "Select item to insert.", "OK", NULL);
1806 return False;
1807 }
1808 /*** check for duplicate object ***/
1809 if (z >= 0) {
1810 op = get_map_ob (self->emap, x, y);
1811 while (op && op->above)
1812 op = op->above;
1813 while (op && zi-- > 0)
1814 op = op->below;
1815 if (op && op->arch == self->app->item.clone->arch)
1816 return False;
1817 }
1818 /* Hopefully this doesn't break anything */
1819 else {
1820 op = get_map_ob (self->emap, x, y);
1821 if (op && op->arch == self->app->item.clone->arch)
1822 return False;
1823 }
1824
1825 debug3("EditInsert() %dx%dx%d\n",x,y,z);
1826
1827 /*** handle background ***/
1828 if (self->overwrite)
1829 while (get_map_ob (self->emap, x, y))
1830 EditDelete(self, x, y, 0);
1831 else {
1832 tmp = MapGetObjectZ (self->emap, x, y, z);
1833 if (tmp &&
1834 (tmp->arch == self->app->item.clone->arch ||
1835 (self->app->item.wall_map &&
1836 find_draw_arch (AppItemGetMap(self->app),
1837 AppItemGetWall(self->app),
1838 tmp->arch) >= 0)))
1839 return False;
1840 }
1841
1842 /*** create & insert ***/
1843 op = EditCloneInsert (self, self->app->item.clone, x, y, z);
1844
1845 if (self->app->item.wall_map)
1846 draw_add (self, x, y);
1847
1848 EditObjectCalc(self,op,&rect);
1849 SET_MAP_FLAGS(self->emap, x, y, P_NEED_UPDATE);
1850 update_position (self->emap, x, y);
1851 CrEditRefresh(self->w,rect);
1852
1853 EditModified(self);
1854 return True;
1855 }
1856
1857
1858 /*
1859 * member: insert object to point of map of editor
1860 * object:
1861 * x,y,z : point to insert
1862 * return: True, if obejct inserted
1863 */
1864 Boolean EditObjectInsert (Edit self,object *obj,int x, int y, int z)
1865 {
1866 int zi = z;
1867 object *op,*tmp;
1868 XRectangle rect;
1869
1870 /*** ***/
1871 if (!self ||
1872 self->read_only ||
1873 !self->emap ||
1874 out_of_map (self->emap, x, y) ||
1875 !obj)
1876 return False;
1877
1878 /*** check for duplicate object ***/
1879 op = get_map_ob (self->emap, x, y);
1880 while (op && op->above)
1881 op = op->above;
1882 while (op && zi-- > 0)
1883 op = op->below;
1884 if (op && op->arch == obj->arch)
1885 return False;
1886
1887 debug3("EditInsert() %dx%dx%d\n",x,y,z);
1888
1889 /*** handle background ***/
1890 tmp = MapGetObjectZ (self->emap, x, y, z);
1891 if (tmp && (tmp->arch == obj->arch))
1892 return False;
1893
1894 /*** create & insert ***/
1895 op = EditCloneInsert (self, op, x, y, z);
1896
1897 EditObjectCalc(self,op,&rect);
1898 CrEditRefresh(self->w,rect);
1899
1900 EditModified(self);
1901 return True;
1902 }
1903
1904 /*
1905 * member: set path name of map
1906 * path : path of file representing map
1907 */
1908 void EditSetPath(Edit self,String path)
1909 {
1910 if(!self) return;
1911 strcpy(self->emap->path,path);
1912 EditUpdate(self);
1913 }
1914
1915 /*** end of Edit.c ***/