Author: esr
Date: Thu Oct 16 00:00:12 2008
New Revision: 30189

URL: http://svn.gna.org/viewcvs/wesnoth?rev=30189&view=rev
Log:
trackplacer: Add infraructure for handling multiple tracks per edit
session.  Controls for this aren't exposed yet.

Modified:
    trunk/data/tools/trackplacer

Modified: trunk/data/tools/trackplacer
URL: 
http://svn.gna.org/viewcvs/wesnoth/trunk/data/tools/trackplacer?rev=30189&r1=30188&r2=30189&view=diff
==============================================================================
--- trunk/data/tools/trackplacer (original)
+++ trunk/data/tools/trackplacer Thu Oct 16 00:00:12 2008
@@ -67,10 +67,15 @@
 # to add new ones, just fill in a dictionary entry.
 imagedir = "data/core/images/"
 default_map = imagedir + "maps/wesnoth.png"
-icon_dictionary = {
+selected_icon_dictionary = {
     "JOURNEY": imagedir + "misc/new-journey.png",
     "BATTLE":  imagedir + "misc/new-battle.png",
     "REST":    imagedir + "misc/flag-red.png",
+    }
+unselected_icon_dictionary = {
+    "JOURNEY": imagedir + "misc/dot-white.png",
+    "BATTLE":  imagedir + "misc/cross-white.png",
+    "REST":    imagedir + "misc/flag-white.png",
     }
 icon_presentation_order = ("JOURNEY", "BATTLE", "REST")
 segmenters = ("BATTLE","REST")
@@ -99,16 +104,23 @@
     return within(x, y, rect) or within(x+xd-1, y, rect) or \
            within(x, y+yd-1, rect) or within(x+xd-1, y+yd+1, rect)
 
-class JourneyTrack:
-    "Represent a journey track on a map."
+class JourneyTracks:
+    "Represent a set of named journey tracks on a map."
     def __init__(self):
-        self.mapfile = None    # Map background of the journey
-        self.track = []                # List of (action, x, y) tuples
+        self.mapfile = None            # Map background of the journey
+        self.tracks = {}               # Dict of lists of (action, x, y) tuples
+        self.selected_id = None
         self.modifications = 0
-        self.saved_track = []
-        self.properties = {'prefix' : 'JOURNEY'}
+        self.track_list = []
+        self.properties = {}
+        self.modified = False
+    def selected_track(self):
+        "Select a track for modification"
+        return self.tracks[self.selected_id]
+    def set_selected_track(self, name):
+        self.selected_id = name
     def write(self, fp):
-        "Record a journey track."
+        "Record a set of named journey tracks."
         if fp.name.endswith(".cfg"):
             fp.write("# Edited by trackplacer on 
%s.\n"%time.ctime(time.time()))
             fp.write("# Hand-hack strictly at your own risk.\n")
@@ -117,29 +129,29 @@
                 fp.write("# trackplacer: %s=%s\n" % (key, val))
             fp.write("# wmllint: no translatables\n")
             fp.write("#\n")
-            index_tuples = zip(range(len(self.track)), self.track)
-            index_tuples = filter(lambda (i, (a, x, y)): a in segmenters,
-                                  index_tuples)
-            endpoints = map(lambda (i, t): i, index_tuples)
-            if self.track[-1][0] not in segmenters:
-                endpoints.append(len(self.track)-1)
-            prefix = self.properties["prefix"]
-            for (i, e) in enumerate(endpoints):
-                fp.write("#define %s_STAGE_%d\n" % (prefix, i+1,))
-                for j in range(0, e+1):
-                    age="OLD"
-                    if i == 0 or j > endpoints[i-1]:
-                        age = "NEW"
-                    waypoint = (age,) + tuple(self.track[j])
-                    fp.write("    {%s_%s %d %d}\n" % waypoint)
+            for (name, track) in self.tracks.items():
+                index_tuples = zip(range(len(track)), track)
+                index_tuples = filter(lambda (i, (a, x, y)): a in segmenters,
+                                      index_tuples)
+                endpoints = map(lambda (i, t): i, index_tuples)
+                if track[-1][0] not in segmenters:
+                    endpoints.append(len(track)-1)
+                for (i, e) in enumerate(endpoints):
+                    fp.write("#define %s_STAGE_%d\n" % (name, i+1,))
+                    for j in range(0, e+1):
+                        age="OLD"
+                        if i == 0 or j > endpoints[i-1]:
+                            age = "NEW"
+                        waypoint = (age,) + tuple(track[j])
+                        fp.write("    {%s_%s %d %d}\n" % waypoint)
+                    fp.write("#enddef\n\n")
+                fp.write("#define %s_COMPLETE\n" % name)
+                for j in range(len(track)):
+                    waypoint = track[j]
+                    fp.write("    {OLD_%s %d %d}\n" % tuple(waypoint))
                 fp.write("#enddef\n\n")
-            fp.write("#define %s_COMPLETE\n" % prefix)
-            for j in range(len(self.track)):
-                waypoint = self.track[j]
-                fp.write("    {OLD_%s %d %d}\n" % tuple(waypoint))
-            fp.write("#enddef\n\n")
             fp.close()
-            self.saved_track = self.track[:]
+            self.modified = False
         else:
             raise IOException("File must have a .trk or .cfg extension.", 
fp.name)
     def read(self, fp):
@@ -149,56 +161,71 @@
                 fp = open(fp)
             except IOError:
                 raise IOException("Cannot read file.", fp)
-        if self.track:
-            raise IOException("Reading with track nonempty.", fp.name)
+        if self.tracks:
+            raise IOException("Reading with tracks nonempty.", fp.name)
         if fp.name.endswith(".png") or fp.name.endswith(".jpg"):
             self.mapfile = self.properties['map'] = fp.name
+            self.selected_id = "JOURNEY"
+            self.tracks[self.selected_id] = []
             return
         if not fp.name.endswith(".cfg"):
             raise IOException("Cannot read this filetype.", fp.name)
         waypoint_re = re.compile("{NEW_(" + "|".join(icon_presentation_order) 
+ ")" \
                                  + " +([0-9]+) +([0-9]+)}")
         property_re = re.compile("# *trackplacer: ([^=]+)=(.*)")
+        define_re = re.compile("#define ([^_]*)")
         for line in fp:
+            # Which track are we appending to?
+            m = re.search(define_re, line)
+            if m:
+                self.selected_id = m.group(1)
+                if self.selected_id not in self.track_list:
+                    self.track_list.append(self.selected_id)
+                    self.tracks[self.selected_id] = []
+                continue
+            # Is this a track marker?
             m = re.search(waypoint_re, line)
             if m:
                 try:
                     tag = m.group(1)
                     x = int(m.group(2))
                     y = int(m.group(3))
-                    self.saved_track.append((tag, x, y))
+                    self.tracks[self.selected_id].append((tag, x, y))
+                    continue
                 except ValueError:
                     raise IOException("Invalid coordinate field.", fp.name, 
i+1)
+            # Is it a property setting?
             m = re.search(property_re, line)
             if m:
                 self.properties[m.group(1)] = m.group(2)
+                continue
         if "map" in self.properties:
             self.mapfile = self.properties['map']
         else:
             raise IOException("Missing map declaration.", fp.name)
         fp.close()
-        self.track = self.saved_track[:]
+        self.modified = False
+    def __getitem__(self, n):
+        return self.tracks[self.selected_id][n]
+    def __setitem__(self, n, v):
+        self.trackself.tracks[self.selected_id][n] = v
     def has_unsaved_changes(self):
-        return self.track != self.saved_track
-    def __getitem__(self, n):
-        return self.track[n]
-    def __setitem__(self, n, v):
-        self.track[n] = v
+        return self.modified
     def neighbors(self, x, y):
-        "Return list of neighbors, enumerated and sorted by distance."
-        candidates = zip(range(len(self.track)), self.track)
+        "Return list of neighbors on selected track, enumerated and sorted by 
distance."
+        neighbors = []
+        candidates = zip(range(len(self.selected_track())), 
self.selected_track())
         candidates.sort(lambda (i1, (a1, x1, y1)), (i2, (a2, x2, y2)): 
cmp(distance(x, y, x1, y1), distance(x, y, x2, y2)))
         return candidates
-
     def find(self, x, y):
-        "Find all track actions at given point."
+        "Find all actions at the given pointin in the selected track."
         candidates = []
-        for (i, (tag, xt, yt)) in enumerate(self.track):
+        for (i, (tag, xt, yt)) in enumerate(self.selected_track()):
             if x == xt and y == yt:
                 candidates.append(i)
         return candidates
     def insert(self, (action, x, y)):
-        "Append a feature to the track."
+        "Insert a feature in the selected track."
         neighbors = self.neighbors(x, y)
         # There are two or more markers and we're not nearest the end one
         if len(neighbors) >= 2 and neighbors[0][0] != len(neighbors)-1:
@@ -206,18 +233,21 @@
             next_closest = neighbors[1]
             # If the neighbors are adjacent, insert between them 
             if abs(closest[0] - next_closest[0]) == 1:
-                self.track.insert(max(closest[0], next_closest[0]), (action, 
x, y))
+                self.selected_track().insert(max(closest[0], next_closest[0]), 
(action, x, y))
                 return
         # Otherwise, append
-        self.track.append((action, x, y))
+        self.selected_track().append((action, x, y))
+        self.modified = True
     def remove(self, x, y):
-        "Remove a feature from the track."
+        "Remove a feature from the selected track."
         found = self.find(x, y)
         if found:
             # Prefer to delete the most recent feature
-            self.track = self.track[:found[-1]] + self.track[found[-1]+1:]
+            track = self.selected_track()
+            track = track[:found[-1]] + track[found[-1]+1:]
+            self.modified = True
     def __str__(self):
-        return self.mapfile + ": " + `self.track`
+        return self.mapfile + ": " + `self.tracks`
 
 class TrackEditorIcon:
     def __init__(self, action, path):
@@ -239,7 +269,7 @@
     def __init__(self, path=None, verbose=False):
         self.verbose = verbose
         # Initialize our info about the map and track 
-        self.journey = JourneyTrack()
+        self.journey = JourneyTracks()
         self.last_read = None
         self.journey.read(path)
         if path.endswith(".cfg"):
@@ -261,13 +291,19 @@
         except:
             self.fatal_error("Error while reading background map %s" % 
self.journey.mapfile)
         # Now get the icons we'll need for scribbling on the map with.
-        self.action_dictionary = {}
         try:
-            for (action, path) in icon_dictionary.items():
+            self.selected_dictionary = {}
+            for (action, path) in selected_icon_dictionary.items():
                 icon = TrackEditorIcon(action, path)
-                self.log("%s icon has size %d, %d" % \
+                self.log("selected %s icon has size %d, %d" % \
                          (action, icon.icon_width, icon.icon_height))
-                self.action_dictionary[action] = icon
+                self.selected_dictionary[action] = icon
+            self.unselected_dictionary = {}
+            for (action, path) in unselected_icon_dictionary.items():
+                icon = TrackEditorIcon(action, path)
+                self.log("unselected %s icon has size %d, %d" % \
+                         (action, icon.icon_width, icon.icon_height))
+                self.unselected_dictionary[action] = icon
         except:
             self.fatal_error("error while reading icons")
 
@@ -296,7 +332,7 @@
         # Fake icon-labeled buttons with liberal use of labels...
         basebutton = None
         for action in icon_presentation_order:
-            icon = self.action_dictionary[action]
+            icon = self.selected_dictionary[action]
             button = gtk.RadioButton(basebutton)
             bbox = gtk.HBox()
             button.add(bbox)
@@ -437,10 +473,11 @@
 
     def box(self, (action, x, y)):
         "Compute the bounding box for an icon of type ACTION at X, Y."
-        return self.action_dictionary[action].bounding_box(x, y)
+        # Assumers selected and unselected icons are the same size
+        return self.selected_dictionary[action].bounding_box(x, y)
 
     def snap_to(self, x, y):
-        "Snap a location to the nearest feature whose bounding box holds it."
+        "Snap a location to the nearest feature on the selected track whose 
bounding box holds it."
         self.log("Neighbors of %d, %d are %s" % (x, y, 
self.journey.neighbors(x, y)))
         for (i, item) in self.journey.neighbors(x, y):
             if within(x, y, self.box(item)):
@@ -450,43 +487,54 @@
 
     def neighbors(self, (action, x, y)):
         "Return all track items with bounding boxes overlapping this one:"
-        rect = self.action_dictionary[action].bounding_box(x, y)
+        rect = self.selected_dictionary[action].bounding_box(x, y)
         return filter(lambda item: overlaps(rect, self.box(item)),
-                      self.journey.track)
+                      self.journey.flattened())
 
     def erase_feature(self, widget, (action, x, y)):
-        "Erase specified icon from the map."
-        neighbors = self.neighbors((action, x, y))
+        "Erase specified (active) icon from the map."
         # Erase all nearby features that might have been damaged.
-        for (na, nx, ny) in neighbors:
-            rect = self.box((na, nx, ny))
-            self.log("Erasing action=%s, dest=%s" % (na, rect))
-            self.refresh_map(*rect)
-            widget.queue_draw_area(*rect)
-        # Redraw all nearby features except what we're erasing.
-        for (na, nx, ny) in neighbors:
-            if x != nx and y != ny:
-                self.log("Redrawing action=%s" % ((na, nx, ny),))
-                self.draw_feature(widget, (na, nx, ny)) 
-
-    def draw_feature(self, widget, (action, x, y)):
+        save_select = self.journey.selected_id
+        for (id, track) in self.journey.tracks:
+            self.journey.set_selected_id(id)
+            neighbors = self.neighbors((action, x, y))
+            for (na, nx, ny) in neighbors:
+                rect = self.box((na, nx, ny))
+                self.log("Erasing action=%s, dest=%s" % (na, rect))
+                self.refresh_map(*rect)
+                widget.queue_draw_area(*rect)
+            # Redraw all nearby features except what we're erasing.
+            for (na, nx, ny) in neighbors:
+                if x != nx and y != ny:
+                    self.log("Redrawing action=%s" % ((na, nx, ny),))
+                    self.draw_feature(widget, (na, nx, ny)) 
+        self.journey.set_selected_id(save_select)
+
+    def draw_feature(self, widget, (action, x, y), selected):
         "Draw specified icon on the map."
         rect = self.box((action, x, y))
         self.log("Drawing action=%s, dest=%s" % (action, rect))
-        self.pixmap.draw_pixbuf(self.default_gc,
-                                self.action_dictionary[action].icon,
-                                0, 0, *rect)
+        if selected:
+            icon = self.selected_dictionary[action].icon
+        else:
+            icon = self.unselected_dictionary[action].icon
+        self.pixmap.draw_pixbuf(self.default_gc, icon, 0, 0, *rect)
         widget.queue_draw_area(*rect)
 
     def redraw(self, widget, delay=0):
         "Redraw the map and tracks."
         self.refresh_map()
-        for item in self.journey.track:
-            self.draw_feature(widget, item)
-            if delay:
-                time.sleep(delay)
-                self.expose_event(widget)
-                gtk.main_iteration(False)
+        # Draw all unselected tracks before the selected one,
+        # so any icons coinciding will be drawn in the right order.
+        for selected in (False, True):
+            for (id, track) in self.journey.tracks.items():
+                if (id == self.journey.selected_id) == selected:
+                    for item in self.journey.tracks[id]:
+                        self.draw_feature(widget, item, selected)
+                        if delay:
+                            time.sleep(delay)
+                            self.expose_event(widget)
+                            gtk.main_iteration(False)
 
     def configure_event(self, widget, event):
         "Create a new backing pixmap of the appropriate size."
@@ -519,7 +567,7 @@
                 return
             # Actual drawing and mutation of the journey track happens here
             if not self.selected and self.action != "DELETE":
-                self.draw_feature(widget, (self.action, x, y))
+                self.draw_feature(widget, (self.action, x, y), True)
                 self.journey.insert((self.action, x, y))
             elif self.selected != None and self.action == "DELETE":
                 (action, x, y) = self.journey.track[self.selected]
@@ -544,14 +592,11 @@
                (action, lx, ly) = self.journey.track[self.selected]
                self.erase_feature(widget, (action, lx, ly))
                self.journey.track[self.selected] = (action, x, y)
+               self.jouney.modified = True
                self.draw_feature(widget, (action, x, y))
                self.log("Track is %s" % self.journey)
         return True
 
-    #
-    # These two functions implement a civilized quit confirmation that
-    # prompts you if you have unsaved changes 
-    #
     def quit(self, w):
         if self.journey.has_unsaved_changes():
             self.quit_check = gtk.Dialog(title="Really quit?",


_______________________________________________
Wesnoth-commits mailing list
[email protected]
https://mail.gna.org/listinfo/wesnoth-commits

Reply via email to