@@ -697,7 +697,7 @@ struct ItemSorter | |||
void Project::Item::sortAlphabetically() | |||
{ | |||
ItemSorter sorter; | |||
node.sort (sorter); | |||
node.sort (sorter, getUndoManager(), true); | |||
} | |||
bool Project::Item::addFile (const File& file, int insertIndex) | |||
@@ -73,9 +73,7 @@ static void sortArray (ElementComparator& comparator, | |||
{ | |||
if (comparator.compareElements (array[i], array [i + 1]) > 0) | |||
{ | |||
const ElementType temp = array [i]; | |||
array [i] = array[i + 1]; | |||
array [i + 1] = temp; | |||
swapVariables (array[i], array[i + 1]); | |||
if (i > firstElement) | |||
i -= 2; | |||
@@ -103,19 +101,14 @@ static void sortArray (ElementComparator& comparator, | |||
if (comparator.compareElements (array[k], array [maxIndex]) > 0) | |||
maxIndex = k; | |||
const ElementType temp = array [maxIndex]; | |||
array [maxIndex] = array[j]; | |||
array [j] = temp; | |||
swapVariables (array[j], array[maxIndex]); | |||
--j; | |||
} | |||
} | |||
else | |||
{ | |||
const int mid = firstElement + (size >> 1); | |||
ElementType temp = array [mid]; | |||
array [mid] = array [firstElement]; | |||
array [firstElement] = temp; | |||
swapVariables (array[mid], array[firstElement]); | |||
int i = firstElement; | |||
int j = lastElement + 1; | |||
@@ -133,14 +126,10 @@ static void sortArray (ElementComparator& comparator, | |||
if (j < i) | |||
break; | |||
temp = array[i]; | |||
array[i] = array[j]; | |||
array[j] = temp; | |||
swapVariables (array[i], array[j]); | |||
} | |||
temp = array [firstElement]; | |||
array [firstElement] = array[j]; | |||
array [j] = temp; | |||
swapVariables (array[j], array[firstElement]); | |||
if (j - 1 - firstElement >= lastElement - i) | |||
{ | |||
@@ -512,6 +512,28 @@ void ValueTree::SharedObject::moveChild (int currentIndex, int newIndex, UndoMan | |||
} | |||
} | |||
void ValueTree::SharedObject::reorderChildren (const ReferenceCountedArray <SharedObject>& newOrder, UndoManager* undoManager) | |||
{ | |||
jassert (newOrder.size() == children.size()); | |||
if (undoManager == 0) | |||
{ | |||
children = newOrder; | |||
sendChildChangeMessage(); | |||
} | |||
else | |||
{ | |||
for (int i = 0; i < children.size(); ++i) | |||
{ | |||
if (children.getUnchecked(i) != newOrder.getUnchecked(i)) | |||
{ | |||
jassert (children.contains (newOrder.getUnchecked(i))); | |||
moveChild (children.indexOf (newOrder.getUnchecked(i)), i, undoManager); | |||
} | |||
} | |||
} | |||
} | |||
bool ValueTree::SharedObject::isEquivalentTo (const SharedObject& other) const | |||
{ | |||
if (type != other.type | |||
@@ -423,22 +423,21 @@ public: | |||
To improve performance, the compareElements() method can be declared as static or const. | |||
@param comparator the comparator to use for comparing elements. | |||
@param retainOrderOfEquivalentItems if this is true, then items | |||
which the comparator says are equivalent will be | |||
kept in the order in which they currently appear | |||
in the array. This is slower to perform, but may | |||
be important in some cases. If it's false, a faster | |||
algorithm is used, but equivalent elements may be | |||
rearranged. | |||
@param undoManager optional UndoManager for storing the changes | |||
@param retainOrderOfEquivalentItems if this is true, then items which the comparator says are | |||
equivalent will be kept in the order in which they currently appear in the array. | |||
This is slower to perform, but may be important in some cases. If it's false, a | |||
faster algorithm is used, but equivalent elements may be rearranged. | |||
*/ | |||
template <typename ElementComparator> | |||
void sort (ElementComparator& comparator, const bool retainOrderOfEquivalentItems = false) | |||
void sort (ElementComparator& comparator, UndoManager* undoManager, bool retainOrderOfEquivalentItems) | |||
{ | |||
if (object != 0) | |||
{ | |||
ReferenceCountedArray <SharedObject> sortedList (object->children); | |||
ComparatorAdapter <ElementComparator> adapter (comparator); | |||
object->children.sort (adapter, retainOrderOfEquivalentItems); | |||
object->sendChildChangeMessage(); | |||
sortedList.sort (adapter, retainOrderOfEquivalentItems); | |||
object->reorderChildren (sortedList, undoManager); | |||
} | |||
} | |||
@@ -491,6 +490,7 @@ private: | |||
void removeChild (int childIndex, UndoManager*); | |||
void removeAllChildren (UndoManager*); | |||
void moveChild (int currentIndex, int newIndex, UndoManager*); | |||
void reorderChildren (const ReferenceCountedArray <SharedObject>& newOrder, UndoManager*); | |||
bool isEquivalentTo (const SharedObject& other) const; | |||
XmlElement* createXml() const; | |||
@@ -33,7 +33,7 @@ | |||
*/ | |||
#define JUCE_MAJOR_VERSION 1 | |||
#define JUCE_MINOR_VERSION 52 | |||
#define JUCE_BUILDNUMBER 77 | |||
#define JUCE_BUILDNUMBER 78 | |||
/** Current Juce version number. | |||
@@ -39,6 +39,9 @@ | |||
just call the post() method to send them, and when they arrive, your | |||
messageCallback() method will automatically be invoked. | |||
Always create an instance of a CallbackMessage on the heap, as it will be | |||
deleted automatically after the message has been delivered. | |||
@see MessageListener, MessageManager, ActionListener, ChangeListener | |||
*/ | |||
class JUCE_API CallbackMessage : public Message | |||
@@ -207,19 +207,9 @@ void DrawableButton::paintButton (Graphics& g, | |||
if (imageToDraw != 0) | |||
{ | |||
if (style == ImageRaw) | |||
{ | |||
imageToDraw->draw (g, 1.0f); | |||
} | |||
else | |||
{ | |||
imageToDraw->drawWithin (g, | |||
imageSpace.getX(), | |||
imageSpace.getY(), | |||
imageSpace.getWidth(), | |||
imageSpace.getHeight(), | |||
RectanglePlacement::centred, | |||
1.0f); | |||
} | |||
imageToDraw->drawWithin (g, imageSpace.toFloat(), RectanglePlacement::centred, 1.0f); | |||
} | |||
} | |||
@@ -69,13 +69,15 @@ void ToolbarButton::paintButtonArea (Graphics& g, | |||
if (getToggleState() && toggledOnImage != 0) | |||
d = toggledOnImage; | |||
const Rectangle<float> area (0.0f, 0.0f, (float) width, (float) height); | |||
if (! isEnabled()) | |||
{ | |||
Image im (Image::ARGB, width, height, true); | |||
{ | |||
Graphics g2 (im); | |||
d->drawWithin (g2, 0, 0, width, height, RectanglePlacement::centred, 1.0f); | |||
d->drawWithin (g2, area, RectanglePlacement::centred, 1.0f); | |||
} | |||
im.desaturate(); | |||
@@ -83,7 +85,7 @@ void ToolbarButton::paintButtonArea (Graphics& g, | |||
} | |||
else | |||
{ | |||
d->drawWithin (g, 0, 0, width, height, RectanglePlacement::centred, 1.0f); | |||
d->drawWithin (g, area, RectanglePlacement::centred, 1.0f); | |||
} | |||
} | |||
@@ -179,7 +179,7 @@ public: | |||
if (current != 0) | |||
{ | |||
registerMouseDown (screenPos, time, current); | |||
registerMouseDown (screenPos, time, current, buttonState); | |||
sendMouseDown (current, screenPos, time); | |||
} | |||
} | |||
@@ -328,16 +328,10 @@ public: | |||
for (int i = 1; i < numElementsInArray (mouseDowns); ++i) | |||
{ | |||
if (mouseDowns[0].time - mouseDowns[i].time < (int) (MouseEvent::getDoubleClickTimeout() * (1.0 + 0.25 * (i - 1))) | |||
&& abs (mouseDowns[0].position.getX() - mouseDowns[i].position.getX()) < 8 | |||
&& abs (mouseDowns[0].position.getY() - mouseDowns[i].position.getY()) < 8) | |||
{ | |||
if (mouseDowns[0].canBePartOfMultipleClickWith (mouseDowns[1], (int) (MouseEvent::getDoubleClickTimeout() * (1.0 + 0.25 * (i - 1))))) | |||
++numClicks; | |||
} | |||
else | |||
{ | |||
break; | |||
} | |||
} | |||
} | |||
@@ -457,13 +451,23 @@ private: | |||
Point<int> position; | |||
int64 time; | |||
Component* component; | |||
ModifierKeys buttons; | |||
bool canBePartOfMultipleClickWith (const RecentMouseDown& other, int maxTimeBetween) const | |||
{ | |||
return time - other.time < maxTimeBetween | |||
&& abs (position.getX() - other.position.getX()) < 8 | |||
&& abs (position.getY() - other.position.getY()) < 8 | |||
&& buttons == other.buttons;; | |||
} | |||
}; | |||
RecentMouseDown mouseDowns[4]; | |||
bool mouseMovedSignificantlySincePressed; | |||
int64 lastTime; | |||
void registerMouseDown (const Point<int>& screenPos, const int64 time, Component* const component) throw() | |||
void registerMouseDown (const Point<int>& screenPos, const int64 time, | |||
Component* const component, const ModifierKeys& modifiers) throw() | |||
{ | |||
for (int i = numElementsInArray (mouseDowns); --i > 0;) | |||
mouseDowns[i] = mouseDowns[i - 1]; | |||
@@ -471,6 +475,7 @@ private: | |||
mouseDowns[0].position = screenPos; | |||
mouseDowns[0].time = time; | |||
mouseDowns[0].component = component; | |||
mouseDowns[0].buttons = modifiers.withOnlyMouseButtons(); | |||
mouseMovedSignificantlySincePressed = false; | |||
} | |||
@@ -87,21 +87,21 @@ void RectanglePlacement::applyTo (double& x, double& y, | |||
} | |||
} | |||
const AffineTransform RectanglePlacement::getTransformToFit (float x, float y, | |||
float w, float h, | |||
const float dx, const float dy, | |||
const float dw, const float dh) const throw() | |||
const AffineTransform RectanglePlacement::getTransformToFit (const Rectangle<float>& source, const Rectangle<float>& destination) const throw() | |||
{ | |||
if (w == 0 || h == 0) | |||
if (source.isEmpty()) | |||
return AffineTransform::identity; | |||
const float scaleX = dw / w; | |||
const float scaleY = dh / h; | |||
float w = source.getWidth(); | |||
float h = source.getHeight(); | |||
const float scaleX = destination.getWidth() / w; | |||
const float scaleY = destination.getHeight() / h; | |||
if ((flags & stretchToFit) != 0) | |||
return AffineTransform::translation (-x, -y) | |||
return AffineTransform::translation (-source.getX(), -source.getY()) | |||
.scaled (scaleX, scaleY) | |||
.translated (dx, dy); | |||
.translated (destination.getX(), destination.getY()); | |||
float scale = (flags & fillDestination) != 0 ? jmax (scaleX, scaleY) | |||
: jmin (scaleX, scaleY); | |||
@@ -115,21 +115,20 @@ const AffineTransform RectanglePlacement::getTransformToFit (float x, float y, | |||
w *= scale; | |||
h *= scale; | |||
float newX = dx; | |||
float newX = destination.getX(); | |||
float newY = destination.getY(); | |||
if ((flags & xRight) != 0) | |||
newX += dw - w; // right | |||
newX += destination.getWidth() - w; // right | |||
else if ((flags & xLeft) == 0) | |||
newX += (dw - w) / 2.0f; // centre | |||
float newY = dy; | |||
newX += (destination.getWidth() - w) / 2.0f; // centre | |||
if ((flags & yBottom) != 0) | |||
newY += dh - h; // bottom | |||
newY += destination.getHeight() - h; // bottom | |||
else if ((flags & yTop) == 0) | |||
newY += (dh - h) / 2.0f; // centre | |||
newY += (destination.getHeight() - h) / 2.0f; // centre | |||
return AffineTransform::translation (-x, -y) | |||
return AffineTransform::translation (-source.getX(), -source.getY()) | |||
.scaled (scale, scale) | |||
.translated (newX, newY); | |||
} | |||
@@ -27,6 +27,7 @@ | |||
#define __JUCE_RECTANGLEPLACEMENT_JUCEHEADER__ | |||
#include "../geometry/juce_AffineTransform.h" | |||
#include "../geometry/juce_Rectangle.h" | |||
//============================================================================== | |||
@@ -143,14 +144,8 @@ public: | |||
/** Returns the transform that should be applied to these source co-ordinates to fit them | |||
into the destination rectangle using the current flags. | |||
*/ | |||
const AffineTransform getTransformToFit (float sourceX, | |||
float sourceY, | |||
float sourceW, | |||
float sourceH, | |||
float destinationX, | |||
float destinationY, | |||
float destinationW, | |||
float destinationH) const throw(); | |||
const AffineTransform getTransformToFit (const Rectangle<float>& source, | |||
const Rectangle<float>& destination) const throw(); | |||
private: | |||
//============================================================================== | |||
@@ -87,19 +87,13 @@ public: | |||
and can either be made as big as possible, or just reduced to fit. | |||
@param g the graphics context to render onto | |||
@param destX top-left of the target rectangle to fit it into | |||
@param destY top-left of the target rectangle to fit it into | |||
@param destWidth size of the target rectangle to fit the image into | |||
@param destHeight size of the target rectangle to fit the image into | |||
@param destArea the target rectangle to fit the drawable into | |||
@param placement defines the alignment and rescaling to use to fit | |||
this object within the target rectangle. | |||
@param opacity the opacity to use, in the range 0 to 1.0 | |||
*/ | |||
void drawWithin (Graphics& g, | |||
int destX, | |||
int destY, | |||
int destWidth, | |||
int destHeight, | |||
const Rectangle<float>& destArea, | |||
const RectanglePlacement& placement, | |||
float opacity) const; | |||
@@ -114,8 +114,8 @@ public: | |||
const RectanglePlacement placement (placementFlags); | |||
newState.transform | |||
= placement.getTransformToFit (vx, vy, vw, vh, | |||
0.0f, 0.0f, newState.width, newState.height) | |||
= placement.getTransformToFit (Rectangle<float> (vx, vy, vw, vh), | |||
Rectangle<float> (0.0f, 0.0f, newState.width, newState.height)) | |||
.followedBy (newState.transform); | |||
} | |||
} | |||
@@ -452,17 +452,12 @@ static NSArray* findDiskBurnerDevices() | |||
NSMutableArray* results = [NSMutableArray array]; | |||
NSArray* devs = [DRDevice devices]; | |||
if (devs != 0) | |||
for (int i = 0; i < [devs count]; ++i) | |||
{ | |||
int num = [devs count]; | |||
int i; | |||
for (i = 0; i < num; ++i) | |||
{ | |||
NSDictionary* dic = [[devs objectAtIndex: i] info]; | |||
NSString* name = [dic valueForKey: DRDeviceProductNameKey]; | |||
if (name != nil) | |||
[results addObject: name]; | |||
} | |||
NSDictionary* dic = [[devs objectAtIndex: i] info]; | |||
NSString* name = [dic valueForKey: DRDeviceProductNameKey]; | |||
if (name != nil) | |||
[results addObject: name]; | |||
} | |||
return results; | |||
@@ -140,7 +140,7 @@ bool File::isHidden() const | |||
static const String getIOSSystemLocation (NSSearchPathDirectory type) | |||
{ | |||
return nsStringToJuce ([NSSearchPathForDirectoriesInDomains (type, NSUserDomainMask, YES) | |||
objectAtIndex:0]); | |||
objectAtIndex: 0]); | |||
} | |||
#endif | |||