/* * TextLayoutHelper.cpp * created for Marathon: Aleph One Copyright (C) 2001 and beyond by Woody Zenfell, III and the "Aleph One" developers. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. This license is contained in the file "COPYING", which is included with this source code; it is available online at http://www.gnu.org/licenses/gpl.html * The code in this file is licensed to you under the GNU GPL. As the copyright holder, * however, I reserve the right to use this code as I see fit, without being bound by the * GPL's terms. This exemption is not intended to apply to modified versions of this file - * if I were to use a modified version, I would be a licensee of whomever modified it, and * thus must observe the GPL terms. * * TextLayoutHelper assists with laying out a set of non-overlapping rectangles. It's not * as smart or as general as it could be, but it works. * * Created by woody on Thu Nov 08 2001. */ #include "TextLayoutHelper.h" #include #include TextLayoutHelper::TextLayoutHelper() { } TextLayoutHelper::~TextLayoutHelper() { removeAllReservations(); } void TextLayoutHelper::removeAllReservations() { CollectionOfReservationEnds::const_iterator i, end; i = mReservationEnds.begin(); end = mReservationEnds.end(); // Whack the reservation structures (only once per pair of reservation ends of course!) for(i = mReservationEnds.begin(); i != end; i++) if(i->mStartOfReservation == false) delete i->mReservation; mReservationEnds.clear(); } int TextLayoutHelper::reserveSpaceFor(int inLeft, unsigned int inWidth, int inLowestBottom, unsigned int inHeight) { Reservation* theReservation = new Reservation; ReservationEnd theLeftEnd; ReservationEnd theRightEnd; theLeftEnd.mHorizontalCoordinate = inLeft; theLeftEnd.mReservation = theReservation; theLeftEnd.mStartOfReservation = true; theRightEnd.mHorizontalCoordinate = inLeft + inWidth; theRightEnd.mReservation = theReservation; theRightEnd.mStartOfReservation = false; // Walk through, finding place for left end. Keep track of reservations opened and not closed. typedef multiset CollectionOfReservationPointers; CollectionOfReservationPointers theReservations; CollectionOfReservationEnds::iterator i = mReservationEnds.begin(); CollectionOfReservationEnds::const_iterator end = mReservationEnds.end(); for( ; i != end; i++) { if(i->mHorizontalCoordinate > theLeftEnd.mHorizontalCoordinate) // Got to an entry that's past our start. Stop. break; if(i->mStartOfReservation) // Start of reservation - we need to track it. theReservations.insert(i->mReservation); else // Reservation going out of scope - forget about it. theReservations.erase(i->mReservation); } // i points to end OR i points to first element after our left end. // Either way, put our left end where i points. i = mReservationEnds.insert(i, theLeftEnd); // Update the vector end. end = mReservationEnds.end(); // Don't get hoisted on our own petard here... i++; // Continue our walk, recording new reservation openings (but not tracking closings). // Reservations that were open when we got here overlap our left side. Reservations that open // before we can stick in our right end overlap our middle or right side. Either way, they // could conflict with us, so we track them. for( ; i != end; i++) { if(i->mHorizontalCoordinate >= theRightEnd.mHorizontalCoordinate) // Found a reservation end outside our right end - stop here. break; // Only track reservation openings. if(i->mStartOfReservation) theReservations.insert(i->mReservation); } // i points to end OR points to first element at or after our right end. // Either way, put our right end where i points. mReservationEnds.insert(i, theRightEnd); // Now, walk through the reservations that overlap us in X-coordinate space. CollectionOfReservationPointers::const_iterator end3 = theReservations.end(); int theCurrentBottom = inLowestBottom; CollectionOfReservationPointers::const_iterator k; do { k = theReservations.begin(); for( ; k != end3; k++) { assert(inHeight == static_cast(static_cast(inHeight))); assert(0 <= static_cast(inHeight)); if(((*k)->mBottom > theCurrentBottom - static_cast(inHeight)) && ((*k)->mTop < theCurrentBottom)) { // Found one that interferes with us. Adjust our current bottom upwards. theCurrentBottom = (*k)->mTop; // We break (I hope this breaks only the for()!) so that the do-while can walk // through the whole list again to make sure our new location doesn't conflict. // I know, should probably sort and be smarter about this, oh well. break; } } } while(k != end3); // We've now found a bottom that interferes with no other rectangle in the reservation system. theReservation->mBottom = theCurrentBottom; theReservation->mTop = theCurrentBottom - inHeight; // theReservation's storage persists. mReservationEnds now has a start and end for the newest reservation. // Tell the caller what we found for him. return theCurrentBottom; }