* when loading an icon via drag&drop, the file will be remembered

for saving later
* added "Clean Up" feature to "Path" menu
* added "Freeze Transformation" to "Shape" menu (will apply the
  current shape transformation onto the path and reset the shape
  transformation to identity)
* small cleanup in ShapeListView
* implemented zooming in CanvasView
* added context menu while editing a path
* implemented undo/redo for transforming points
  (press T or use context menu)
* added "Split Points" feature (path editing context menu), it will
  make two points from one control point
* improved selecting path points with selection rect
* improved SVG import for zuMis BeOS icons (more precise scale)



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18800 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus
2006-09-10 22:31:09 +00:00
parent 5c22d92129
commit f4bd80a2dc
30 changed files with 1139 additions and 54 deletions
+142 -2
View File
@@ -28,8 +28,8 @@ CanvasView::CanvasView(BRect frame)
fRenderer(new IconRenderer(fBitmap)),
fDirtyIconArea(fBitmap->Bounds()),
fCanvasOrigin(50.0, 50.0),
fZoomLevel(8.0),
fCanvasOrigin(0.0, 0.0),
fZoomLevel(1.0),
fMouseFilterMode(SNAPPING_OFF),
@@ -68,6 +68,8 @@ CanvasView::AttachedToWindow()
SetHighColor(kStripesLow);
_AllocBackBitmap(Bounds().Width(), Bounds().Height());
_SetZoom(8.0);
}
// FrameResized
@@ -140,6 +142,25 @@ CanvasView::MouseMoved(BPoint where, uint32 transit,
// #pragma mark -
// ScrollOffsetChanged
void
CanvasView::ScrollOffsetChanged(BPoint oldOffset, BPoint newOffset)
{
BPoint offset = newOffset - oldOffset;
ScrollBy(offset.x, offset.y);
MouseMoved(fMouseInfo.position + offset, fMouseInfo.transit, NULL);
}
// VisibleSizeChanged
void
CanvasView::VisibleSizeChanged(float oldWidth, float oldHeight,
float newWidth, float newHeight)
{
}
// #pragma mark -
// AreaInvalidated
void
CanvasView::AreaInvalidated(const BRect& area)
@@ -240,6 +261,13 @@ CanvasView::_HandleKeyDown(uint32 key, uint32 modifiers)
CommandStack()->Undo();
break;
case '+':
_SetZoom(_NextZoomInLevel(fZoomLevel));
break;
case '-':
_SetZoom(_NextZoomOutLevel(fZoomLevel));
break;
default:
return StateView::_HandleKeyDown(key, modifiers);
}
@@ -446,3 +474,115 @@ CanvasView::_MakeBackground()
}
}
// #pragma mark -
// _NextZoomInLevel
double
CanvasView::_NextZoomInLevel(double zoom) const
{
if (zoom < 1)
return 1;
if (zoom < 1.5)
return 1.5;
if (zoom < 2)
return 2;
if (zoom < 3)
return 3;
if (zoom < 4)
return 4;
if (zoom < 6)
return 6;
if (zoom < 8)
return 8;
if (zoom < 16)
return 16;
if (zoom < 32)
return 32;
return 64;
}
// _NextZoomOutLevel
double
CanvasView::_NextZoomOutLevel(double zoom) const
{
if (zoom > 32)
return 32;
if (zoom > 16)
return 16;
if (zoom > 8)
return 8;
if (zoom > 6)
return 6;
if (zoom > 4)
return 4;
if (zoom > 3)
return 3;
if (zoom > 2)
return 2;
if (zoom > 1.5)
return 1.5;
return 1;
}
// _SetZoom
void
CanvasView::_SetZoom(double zoomLevel)
{
if (fZoomLevel == zoomLevel)
return;
// zoom into mouse position, or into center of view
BPoint anchor = MouseInfo()->position;
BRect bounds(Bounds());
if (!bounds.Contains(anchor)) {
bounds = _CanvasRect();
anchor.x = (bounds.left + bounds.right) / 2.0;
anchor.y = (bounds.top + bounds.bottom) / 2.0;
}
printf("anchor: %.2f, %.2f\n", anchor.x, anchor.y);
BPoint offset;
if (fZoomLevel < zoomLevel) {
offset.x = anchor.x * (zoomLevel / fZoomLevel) - anchor.x;
offset.y = anchor.y * (zoomLevel / fZoomLevel) - anchor.y;
} else {
offset.x = -anchor.x * (zoomLevel / fZoomLevel);
offset.y = -anchor.y * (zoomLevel / fZoomLevel);
}
fZoomLevel = zoomLevel;
SetDataRect(_LayoutCanvas());
printf("offset: %.2f, %.2f\n", offset.x, offset.y);
SetScrollOffset(ScrollOffset() + offset);
Invalidate();
}
// _LayoutCanvas
BRect
CanvasView::_LayoutCanvas()
{
if (!fBitmap)
return BRect(0, 0, -1, -1);
// size of zoomed bitmap
BRect r(fBitmap->Bounds());
r.OffsetTo(0, 0);
r.right = floorf((r.Width() + 1) * fZoomLevel + 0.5) - 1;
r.bottom = floorf((r.Height() + 1) * fZoomLevel + 0.5) - 1;
// TODO: ask manipulators to extend size
// left top of canvas within empty area
fCanvasOrigin.x = floorf(r.Width() * 0.25);
fCanvasOrigin.y = floorf(r.Height() * 0.25);
// resize for empty area around bitmap
r.right += r.Width() * 0.5;
r.bottom += r.Height() * 0.5;
return r;
}
+17 -1
View File
@@ -10,6 +10,7 @@
#define CANVAS_VIEW_H
#include "Icon.h"
#include "Scrollable.h"
#include "StateView.h"
class BBitmap;
@@ -23,6 +24,7 @@ enum {
};
class CanvasView : public StateView,
public Scrollable,
public IconListener {
public:
CanvasView(BRect frame);
@@ -37,7 +39,16 @@ class CanvasView : public StateView,
virtual void MouseMoved(BPoint where, uint32 transit,
const BMessage* dragMessage);
// Scrollable interface
protected:
virtual void ScrollOffsetChanged(BPoint oldOffset,
BPoint newOffset);
virtual void VisibleSizeChanged(float oldWidth,
float oldHeight,
float newWidth,
float newHeight);
// IconListener interface
public:
virtual void AreaInvalidated(const BRect& area);
// CanvasView
@@ -74,6 +85,11 @@ class CanvasView : public StateView,
void _MakeBackground();
private:
double _NextZoomInLevel(double zoom) const;
double _NextZoomOutLevel(double zoom) const;
void _SetZoom(double zoomLevel);
BRect _LayoutCanvas();
BBitmap* fBitmap;
BBitmap* fBackground;
@@ -82,7 +98,7 @@ class CanvasView : public StateView,
BRect fDirtyIconArea;
BPoint fCanvasOrigin;
float fZoomLevel;
double fZoomLevel;
uint32 fMouseFilterMode;
+71
View File
@@ -0,0 +1,71 @@
/*
* Icon-O-Matic.rdef
*/
resource app_signature "application/x-vnd.haiku-icon_o_matic";
resource app_version {
major = 1,
middle = 0,
minor = 0,
/* 0 = development 1 = alpha 2 = beta
3 = gamma 4 = golden master 5 = final */
variety = 2,
internal = 0,
short_info = "Icon-O-Matic",
long_info = "Icon-O-Matic 1.0.0 ©2006 Haiku"
};
resource app_flags B_SINGLE_LAUNCH;
//resource file_types message {
// "types" = "application/x-haiku_icon",
//};
resource(101, "BEOS:ICON") #'RAWT' array {
$"6E6369660E050102000603399E0F3D9C0ABF82B23B84A94B88504910C900A5B1"
$"FFBCEAF1FFFFB3B8FF020106023E49240000000000003CAAAA4940004A8000FF"
$"C0D5FF7C896EFF040192020006023B8E380000000000004000004AAA003A0000"
$"002B30A0FFCBDCFA038089D80200060237889D389554BBFC4A3AB9D048DADE4A"
$"322000FFF9BAFFFFC10402000602B5B574397997BC15F4B839E3489DF94B8820"
$"00C7E3FFFF392FFF0200060237A0CC393C5ABC1AC23A5C164A39B94B1E5400FD"
$"B9B9FFCE3232020006023D4D340000000000004000004A50000000000097A3BA"
$"FFF8FBFF020006023C08200000000000004000004A4000000000FF7E94B4005F"
$"6C9302000602B507E13A82E2BAD599B56BB44A7652479FE4002B2D3DFE444C6D"
$"02000603B2F679BA14D43A7FB6B38E9E474F5546D05A00F0CD9B81C0995DCFF0"
$"D8B502000602AAB1FB3A081FBE8A26AF5E794C4014448D43FFFDDCAB00DBAB5F"
$"140606AE0BB40BC14B33C5ADB75DC371BDEFC805C13ECA02CA28BF80C118BB1E"
$"C51BBD3EBF07BA063AB8BA060CAEAABAB40BC14B33C5ADB75DC371BDEFC805C1"
$"3ECA02C4E04F41C507374B3A4945C33CC6D1C36FCA28BF80C118BB1EC51BBD3E"
$"BF07BA063AB8BA0605AE02B57D43B9B9C5EDB7BB49BBB756BD75CB34CA8EC3AF"
$"40340609AEAA02B57D43B9B9C5EDB7BB49BBB756BD75CB34C6F0C5C24E4F514B"
$"C9BBC428CA8EC3AF40340A093B5E3D60BFCDCB3C4560C516C7EF604B5B485D4A"
$"44560A045A475E445A4257450A06C4E04F41C507374B3A4945C33CC6D1C36F0A"
$"04C6F0C5C24E4F514BC9BBC4280A062A452B4D3251364D35452F420A06245225"
$"5A2B5E315A31522A4E0A053153315B375F3C5B3C530606B20831245356295625"
$"562D532E3126290A063124B8BAB5B2B8BAB779312EB677B6ECB67CB63C0804BA"
$"28B4D33027302BBA28B8580A043324B969B5BA4E2751240A04B969B5BAB969B7"
$"714E2B4E270A04B969B771332E512E4E2B0003C6E8B4D3C6E8B4D3C690B51750"
$"2950B59E50B78DC6E8B858C690B814C6E8B8580604EE532456295625562D532E"
$"5029502D50250604EE532755295528552A532B5229522A5228120A0302040500"
$"0A0001021001178400040A020103000A050107000A0001001001178400040A01"
$"0101000A000108123E7578BF27AD3F27AD3E7578C831624850E201178400040A"
$"060108023E7578BF27AD3F27AD3E7578C831624850E20A070109023E7578BF27"
$"AD3F27AD3E7578C831624850E20A08010A023E7578BF27AD3F27AD3E7578C831"
$"624850E20A040106000A00030B0D11123ED413BED4133ED4133ED41347F4A24A"
$"588901178400040A0C010C023ED413BED4133ED4133ED41347F4A24A58890A09"
$"010E023ED413BED4133ED4133ED41347F4A24A58890A0A010F023ED413BED413"
$"3ED4133ED41347F4A24A58890A0B0110023ED413BED4133ED4133ED41347F4A2"
$"4A58890A0D0112023ED413BED4133ED4133ED41347F4A24A58890A0001130A3E"
$"D413BED4133ED4133ED41347F4A24A588915FF"
};
//resource large_icon array {
//};
//
//resource mini_icon array {
//};
+20 -3
View File
@@ -39,7 +39,7 @@ using std::nothrow;
// constructor
IconEditorApp::IconEditorApp()
: BApplication("application/x-vnd.Haiku-Icon-O-Matic"),
: BApplication("application/x-vnd.haiku-icon_o_matic"),
fMainWindow(NULL),
fDocument(new Document("test")),
@@ -275,17 +275,24 @@ IconEditorApp::_Open(const entry_ref& ref, bool append)
if (!icon)
return;
enum {
REF_NONE = 0,
REF_MESSAGE,
REF_FLAT
};
uint32 refMode = REF_NONE;
// try different file types
FlatIconImporter flatImporter;
status_t ret = flatImporter.Import(icon, &file);
if (ret >= B_OK) {
fDocument->SetExportRef(ref);
refMode = REF_FLAT;
} else {
file.Seek(0, SEEK_SET);
MessageImporter msgImporter;
ret = msgImporter.Import(icon, &file);
if (ret >= B_OK) {
fDocument->SetRef(ref);
refMode = REF_MESSAGE;
} else {
file.Seek(0, SEEK_SET);
SVGImporter svgImporter;
@@ -315,8 +322,18 @@ IconEditorApp::_Open(const entry_ref& ref, bool append)
fMainWindow->SetIcon(NULL);
fDocument->MakeEmpty();
fDocument->SetIcon(icon);
switch (refMode) {
case REF_MESSAGE:
fDocument->SetRef(ref);
break;
case REF_FLAT:
fDocument->SetExportRef(ref);
break;
}
locker.Unlock();
if (mainWindowLocked) {
+6
View File
@@ -231,6 +231,8 @@ Application Icon-O-Matic :
AddShapesCommand.cpp
AddTransformersCommand.cpp
ChangePointCommand.cpp
CleanUpPathCommand.cpp
FreezeTransformationCommand.cpp
InsertPointCommand.cpp
MoveShapesCommand.cpp
MovePathsCommand.cpp
@@ -242,6 +244,8 @@ Application Icon-O-Matic :
RemoveShapesCommand.cpp
RemoveTransformersCommand.cpp
ReversePathCommand.cpp
SplitPointsCommand.cpp
TransformPointsCommand.cpp
UnassignPathCommand.cpp
# style
@@ -274,6 +278,8 @@ Application Icon-O-Matic :
Util.cpp
: be tracker translation libagg.a libexpat.a
: Icon-O-Matic.rdef
;
+16 -3
View File
@@ -247,6 +247,8 @@ case MSG_SHAPE_SELECTED: {
Shape* shape;
if (message->FindPointer("shape", (void**)&shape) < B_OK)
shape = NULL;
if (!fIcon || !fIcon->Shapes()->HasShape(shape))
shape = NULL;
fPathListView->SetCurrentShape(shape);
fStyleListView->SetCurrentShape(shape);
@@ -707,10 +709,21 @@ MainWindow::_CreateGUI(BRect bounds)
// canvas view
bounds.left = splitWidth;
bounds.top = fSwatchGroup->Frame().bottom + 1;
bounds.right = bg->Bounds().right;
bounds.bottom = bg->Bounds().bottom;
bounds.right = bg->Bounds().right - B_V_SCROLL_BAR_WIDTH;
bounds.bottom = bg->Bounds().bottom - B_H_SCROLL_BAR_HEIGHT;
fCanvasView = new CanvasView(bounds);
// scroll view around canvas view
bounds.bottom += B_H_SCROLL_BAR_HEIGHT;
bounds.right += B_V_SCROLL_BAR_WIDTH;
ScrollView* canvasScrollView
= new ScrollView(fCanvasView,
SCROLL_HORIZONTAL | SCROLL_VERTICAL
| SCROLL_VISIBLE_RECT_IS_CHILD_BOUNDS
| SCROLL_NO_FRAME,
bounds, "canvas scroll view",
B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS);
// icon previews
bounds.left = 5;
bounds.top = fSwatchGroup->Frame().top + 5;
@@ -882,7 +895,7 @@ MainWindow::_CreateGUI(BRect bounds)
B_WILL_DRAW | B_FRAME_EVENTS));
bg->AddChild(fCanvasView);
bg->AddChild(canvasScrollView);
#endif // __HAIKU__
}
@@ -68,6 +68,13 @@ Manipulator::DoubleClicked(BPoint where)
return false;
}
// ShowContextMenu
bool
Manipulator::ShowContextMenu(BPoint where)
{
return false;
}
// #pragma mark -
bool
@@ -33,6 +33,8 @@ class Manipulator : public Observer {
virtual bool MouseOver(BPoint where);
virtual bool DoubleClicked(BPoint where);
virtual bool ShowContextMenu(BPoint where);
virtual bool MessageReceived(BMessage* message,
Command** _command);
@@ -80,7 +80,10 @@ MultipleManipulatorState::MessageReceived(BMessage* message,
void
MultipleManipulatorState::MouseDown(BPoint where, uint32 buttons, uint32 clicks)
{
// NOTE: buttons currently ignored
if (buttons & B_SECONDARY_MOUSE_BUTTON) {
_ShowContextMenu(where);
return;
}
if (clicks == 2
&& fPreviousManipulator
@@ -281,3 +284,18 @@ MultipleManipulatorState::_UpdateCursor()
else
fView->SetViewCursor(B_CURSOR_SYSTEM_DEFAULT);
}
// _ShowContextMenu
void
MultipleManipulatorState::_ShowContextMenu(BPoint where)
{
int32 count = fManipulators.CountItems();
for (int32 i = 0; i < count; i++) {
Manipulator* manipulator =
(Manipulator*)fManipulators.ItemAtFast(i);
if (manipulator->ShowContextMenu(where))
return;
}
}
@@ -55,6 +55,8 @@ class MultipleManipulatorState : public ViewState {
private:
void _UpdateCursor();
void _ShowContextMenu(BPoint where);
BList fManipulators;
Manipulator* fCurrentManipulator;
@@ -12,7 +12,7 @@
mouse_info::mouse_info()
: buttons(0),
position(B_ORIGIN),
position(-1, -1),
transit(B_OUTSIDE_VIEW),
modifiers(::modifiers())
{
@@ -51,7 +51,6 @@ class ViewState {
// modifiers
virtual void ModifiersChanged(uint32 modifiers);
// TODO: mouse wheel
virtual bool HandleKeyDown(uint32 key, uint32 modifiers,
Command** _command);
@@ -19,6 +19,7 @@
#include <Window.h>
#include "AddPathsCommand.h"
#include "CleanUpPathCommand.h"
#include "CommandStack.h"
#include "MovePathsCommand.h"
#include "Observer.h"
@@ -236,6 +237,7 @@ enum {
MSG_DUPLICATE = 'dupp',
MSG_REVERSE = 'rvrs',
MSG_CLEAN_UP = 'clup',
MSG_ROTATE_INDICES = 'roti',
MSG_REMOVE = 'remp',
@@ -430,6 +432,19 @@ PathListView::MessageReceived(BMessage* message)
}
break;
case MSG_CLEAN_UP:
if (fCommandStack) {
PathListItem* item = dynamic_cast<PathListItem*>(
ItemAt(CurrentSelection(0)));
if (!item)
break;
CleanUpPathCommand* command
= new (nothrow) CleanUpPathCommand(item->path);
fCommandStack->Perform(command);
}
break;
case MSG_REMOVE:
RemoveSelected();
break;
@@ -701,6 +716,7 @@ PathListView::SetMenu(BMenu* menu)
new BMessage(MSG_ADD_ARC));
fDuplicateMI = new BMenuItem("Duplicate", new BMessage(MSG_DUPLICATE));
fReverseMI = new BMenuItem("Reverse", new BMessage(MSG_REVERSE));
fCleanUpMI = new BMenuItem("Clean Up", new BMessage(MSG_CLEAN_UP));
fRotateIndicesMI = new BMenuItem("Rotate Indices",
new BMessage(MSG_ROTATE_INDICES));
fRemoveMI = new BMenuItem("Remove", new BMessage(MSG_REMOVE));
@@ -715,6 +731,7 @@ fAddArcMI->SetEnabled(false);
fMenu->AddItem(fDuplicateMI);
fMenu->AddItem(fReverseMI);
fMenu->AddItem(fCleanUpMI);
fMenu->AddItem(fRotateIndicesMI);
fMenu->AddSeparatorItem();
+1
View File
@@ -86,6 +86,7 @@ class PathListView : public SimpleListView,
BMenuItem* fAddArcMI;
BMenuItem* fDuplicateMI;
BMenuItem* fReverseMI;
BMenuItem* fCleanUpMI;
BMenuItem* fRotateIndicesMI;
BMenuItem* fRemoveMI;
+36 -11
View File
@@ -23,6 +23,7 @@
#include "AddShapesCommand.h"
#include "AddStylesCommand.h"
#include "CommandStack.h"
#include "FreezeTransformationCommand.h"
#include "MoveShapesCommand.h"
#include "Observer.h"
#include "RemoveShapesCommand.h"
@@ -95,6 +96,7 @@ enum {
MSG_REMOVE = 'rmsh',
MSG_DUPLICATE = 'dpsh',
MSG_RESET_TRANSFORMATION = 'rstr',
MSG_FREEZE_TRANSFORMATION = 'frzt',
MSG_DRAG_SHAPE = 'drgs',
};
@@ -165,17 +167,9 @@ ShapeListView::MessageReceived(BMessage* message)
break;
}
case MSG_RESET_TRANSFORMATION: {
int32 count = CountSelectedItems();
BList shapes;
for (int32 i = 0; i < count; i++) {
ShapeListItem* item = dynamic_cast<ShapeListItem*>(
ItemAt(CurrentSelection(i)));
if (item && item->shape) {
if (!shapes.AddItem((void*)item->shape))
break;
}
}
count = shapes.CountItems();
_GetSelectedShapes(shapes);
int32 count = shapes.CountItems();
if (count < 0)
break;
@@ -191,6 +185,20 @@ ShapeListView::MessageReceived(BMessage* message)
fCommandStack->Perform(command);
break;
}
case MSG_FREEZE_TRANSFORMATION: {
BList shapes;
_GetSelectedShapes(shapes);
int32 count = shapes.CountItems();
if (count < 0)
break;
FreezeTransformationCommand* command =
new FreezeTransformationCommand((Shape**)shapes.Items(),
count);
fCommandStack->Perform(command);
break;
}
default:
SimpleListView::MessageReceived(message);
break;
@@ -433,6 +441,8 @@ ShapeListView::SetMenu(BMenu* menu)
fDuplicateMI = new BMenuItem("Duplicate", new BMessage(MSG_DUPLICATE));
fResetTransformationMI = new BMenuItem("Reset Transformation",
new BMessage(MSG_RESET_TRANSFORMATION));
fFreezeTransformationMI = new BMenuItem("Freeze Transformation",
new BMessage(MSG_FREEZE_TRANSFORMATION));
fRemoveMI = new BMenuItem("Remove", new BMessage(MSG_REMOVE));
@@ -446,6 +456,7 @@ ShapeListView::SetMenu(BMenu* menu)
fMenu->AddItem(fDuplicateMI);
fMenu->AddItem(fResetTransformationMI);
fMenu->AddItem(fFreezeTransformationMI);
fMenu->AddSeparatorItem();
@@ -453,6 +464,7 @@ ShapeListView::SetMenu(BMenu* menu)
fDuplicateMI->SetTarget(this);
fResetTransformationMI->SetTarget(this);
fFreezeTransformationMI->SetTarget(this);
fRemoveMI->SetTarget(this);
_UpdateMenu();
@@ -541,4 +553,17 @@ ShapeListView::_UpdateMenu()
fRemoveMI->SetEnabled(gotSelection);
}
// _GetSelectedShapes
void
ShapeListView::_GetSelectedShapes(BList& shapes) const
{
int32 count = CountSelectedItems();
for (int32 i = 0; i < count; i++) {
ShapeListItem* item = dynamic_cast<ShapeListItem*>(
ItemAt(CurrentSelection(i)));
if (item && item->shape) {
if (!shapes.AddItem((void*)item->shape))
break;
}
}
}
@@ -68,6 +68,8 @@ class ShapeListView : public SimpleListView,
ShapeListItem* _ItemForShape(Shape* shape) const;
void _UpdateMenu();
void _GetSelectedShapes(BList& shapes) const;
BMessage* fMessage;
ShapeContainer* fShapeContainer;
@@ -80,6 +82,7 @@ class ShapeListView : public SimpleListView,
BMenuItem* fAddWidthPathAndStyleMI;
BMenuItem* fDuplicateMI;
BMenuItem* fResetTransformationMI;
BMenuItem* fFreezeTransformationMI;
BMenuItem* fRemoveMI;
};
@@ -477,19 +477,27 @@ DocumentBuilder::GetIcon(Icon* icon, SVGImporter* importer,
yMax = ceil(yMax);
BRect bounds;
if (fViewBox.IsValid())
if (fViewBox.IsValid()) {
bounds = fViewBox;
else
printf("view box: ");
bounds.PrintToStream();
} else {
bounds.Set(0.0, 0.0, (int32)fWidth - 1, (int32)fHeight - 1);
printf("width/height: ");
bounds.PrintToStream();
}
BRect boundingBox(xMin, yMin, xMax, yMax);
if (!bounds.IsValid() || !boundingBox.Intersects(bounds)) {
bounds = boundingBox;
printf("using bounding box: ");
bounds.PrintToStream();
}
float size = max_c(bounds.Width() + 1.0, bounds.Height() + 1.0);
float size = min_c(bounds.Width() + 1.0, bounds.Height() + 1.0);
double scale = 64.0 / size;
printf("scale: %f\n", scale);
Transformable transform;
transform.TranslateBy(BPoint(-bounds.left, -bounds.top));
@@ -540,6 +548,8 @@ DocumentBuilder::EndGradient()
fCurrentGradient = NULL;
}
// #pragma mark -
// _AddGradient
void
DocumentBuilder::_AddGradient(SVGGradient* gradient)
+82 -13
View File
@@ -13,6 +13,8 @@
#include <Cursor.h>
#include <Message.h>
#include <MenuItem.h>
#include <PopUpMenu.h>
#include <Window.h>
#include "cursors.h"
@@ -31,6 +33,7 @@
//#include "ReversePathCommand.h"
//#include "SelectPathCommand.h"
//#include "SelectPointsCommand.h"
#include "SplitPointsCommand.h"
#include "TransformPointsBox.h"
#define POINT_EXTEND 3.0
@@ -65,6 +68,14 @@ enum {
SELECT_SUB_PATH,
};
enum {
MSG_TRANSFORM = 'strn',
MSG_REMOVE_POINTS = 'srmp',
MSG_UPDATE_SHAPE_UI = 'udsi',
MSG_SPLIT_POINTS = 'splt',
};
inline const char*
string_for_mode(uint32 mode)
{
@@ -480,18 +491,29 @@ PathManipulator::MouseDown(BPoint where)
_RemovePointOut(fCurrentPathPoint);
break;
case SELECT_POINTS:
if (!fShiftDown) {
case SELECT_POINTS: {
// TODO: this works so that you can deselect all points
// when clicking outside the path even if pressing shift
// in case the path is open... a better way would be
// to deselect all on mouse up, if the mouse has not moved
bool appendSelection;
if (fPath->IsClosed())
appendSelection = fShiftDown;
else
appendSelection = fShiftDown && fCurrentPathPoint >= 0;
if (!appendSelection) {
fSelection->MakeEmpty();
_UpdateSelection();
}
*fOldSelection = *fSelection;
if (fCurrentPathPoint >= 0) {
_Select(fCurrentPathPoint, fShiftDown);
_Select(fCurrentPathPoint, appendSelection);
}
fCanvasView->BeginRectTracking(BRect(where, where),
B_TRACK_RECT_CORNER);
break;
}
}
fTrackingStart = canvasWhere;
@@ -707,6 +729,52 @@ PathManipulator::DoubleClicked(BPoint where)
return false;
}
// ShowContextMenu
bool
PathManipulator::ShowContextMenu(BPoint where)
{
BPopUpMenu* menu = new BPopUpMenu("context menu", false, false);
BMessage* message;
BMenuItem* item;
bool hasSelection = fSelection->CountItems() > 0;
message = new BMessage(B_SELECT_ALL);
item = new BMenuItem("Select All", message, 'A');
menu->AddItem(item);
menu->AddSeparatorItem();
message = new BMessage(MSG_TRANSFORM);
item = new BMenuItem("Transform", message);
item->SetEnabled(hasSelection);
menu->AddItem(item);
message = new BMessage(MSG_SPLIT_POINTS);
item = new BMenuItem("Split", message);
item->SetEnabled(hasSelection);
menu->AddItem(item);
message = new BMessage(MSG_REMOVE_POINTS);
item = new BMenuItem("Remove", message, 'A');
item->SetEnabled(hasSelection);
menu->AddItem(item);
// go
menu->SetTargetForItems(fCanvasView);
menu->SetAsyncAutoDestruct(true);
menu->SetFont(be_plain_font);
where = fCanvasView->ConvertToScreen(where);
BRect mouseRect(where, where);
mouseRect.InsetBy(-10.0, -10.0);
where += BPoint(5.0, 5.0);
menu->Go(where, true, false, mouseRect, true);
return true;
}
// #pragma mark -
// Bounds
BRect
PathManipulator::Bounds()
@@ -725,25 +793,24 @@ PathManipulator::TrackingBounds(BView* withinView)
// #pragma mark -
enum {
MSG_SHAPE_TRANSFORM = 'strn',
MSG_SHAPE_REMOVE_POINTS = 'srmp',
MSG_UPDATE_SHAPE_UI = 'udsi',
};
// MessageReceived
bool
PathManipulator::MessageReceived(BMessage* message, Command** _command)
{
bool result = true;
switch (message->what) {
case MSG_SHAPE_TRANSFORM:
case MSG_TRANSFORM:
if (!fSelection->IsEmpty())
_SetMode(TRANSFORM_POINTS);
break;
case MSG_SHAPE_REMOVE_POINTS:
case MSG_REMOVE_POINTS:
*_command = _Delete();
break;
case MSG_SPLIT_POINTS:
*_command = new SplitPointsCommand(fPath,
fSelection->Items(),
fSelection->CountItems());
break;
case B_SELECT_ALL: {
*fOldSelection = *fSelection;
fSelection->MakeEmpty();
@@ -1292,10 +1359,12 @@ void
PathManipulator::_Select(BRect r)
{
BPoint p;
BPoint pIn;
BPoint pOut;
int32 count = fPath->CountPoints();
Selection temp;
for (int32 i = 0; i < count && fPath->GetPointAt(i, p); i++) {
if (r.Contains(p)) {
for (int32 i = 0; i < count && fPath->GetPointsAt(i, p, pIn, pOut); i++) {
if (r.Contains(p) || r.Contains(pIn) || r.Contains(pOut)) {
temp.Add(i);
}
}
@@ -45,6 +45,8 @@ class PathManipulator : public Manipulator,
virtual bool MouseOver(BPoint where);
virtual bool DoubleClicked(BPoint where);
virtual bool ShowContextMenu(BPoint where);
virtual BRect Bounds();
virtual BRect TrackingBounds(BView* withinView);
@@ -0,0 +1,52 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "CleanUpPathCommand.h"
#include <stdio.h>
#include "VectorPath.h"
// constructor
CleanUpPathCommand::CleanUpPathCommand(VectorPath* path)
: PathCommand(path),
fOriginalPath()
{
if (fPath)
fOriginalPath = *fPath;
}
// destructor
CleanUpPathCommand::~CleanUpPathCommand()
{
}
// Perform
status_t
CleanUpPathCommand::Perform()
{
fPath->CleanUp();
return B_OK;
}
// Undo
status_t
CleanUpPathCommand::Undo()
{
*fPath = fOriginalPath;
return B_OK;
}
// GetName
void
CleanUpPathCommand::GetName(BString& name)
{
name << "Clean Up Path";
}
@@ -0,0 +1,29 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef CLEAN_UP_PATH_COMMAND_H
#define CLEAN_UP_PATH_COMMAND_H
#include "PathCommand.h"
#include "VectorPath.h"
class CleanUpPathCommand : public PathCommand {
public:
CleanUpPathCommand(VectorPath* path);
virtual ~CleanUpPathCommand();
virtual status_t Perform();
virtual status_t Undo();
virtual void GetName(BString& name);
private:
VectorPath fOriginalPath;
};
#endif // CLEAN_UP_PATH_COMMAND_H
@@ -0,0 +1,152 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "FreezeTransformationCommand.h"
#include <new>
#include <stdio.h>
#include <string.h>
#include "Gradient.h"
#include "Shape.h"
#include "Style.h"
#include "VectorPath.h"
using std::nothrow;
// constructor
FreezeTransformationCommand::FreezeTransformationCommand(
Shape** const shapes,
int32 count)
: Command(),
fShapes(shapes && count > 0 ? new (nothrow) Shape*[count] : NULL),
fOriginalTransformations(count > 0 ? new (nothrow) double[
count * Transformable::matrix_size]
: NULL),
fCount(count)
{
if (!fShapes || !fOriginalTransformations)
return;
memcpy(fShapes, shapes, sizeof(Shape*) * fCount);
bool initOk = false;
for (int32 i = 0; i < fCount; i++) {
if (!fShapes[i])
continue;
if (!fShapes[i]->IsIdentity())
initOk = true;
fShapes[i]->StoreTo(&fOriginalTransformations[
i * Transformable::matrix_size]);
}
if (!initOk) {
delete[] fShapes;
fShapes = NULL;
delete[] fOriginalTransformations;
fOriginalTransformations = NULL;
}
}
// destructor
FreezeTransformationCommand::~FreezeTransformationCommand()
{
delete[] fShapes;
delete[] fOriginalTransformations;
}
// InitCheck
status_t
FreezeTransformationCommand::InitCheck()
{
return fShapes && fOriginalTransformations ? B_OK : B_NO_INIT;
}
// Perform
status_t
FreezeTransformationCommand::Perform()
{
for (int32 i = 0; i < fCount; i++) {
if (!fShapes[i] || fShapes[i]->IsIdentity())
continue;
_ApplyTransformation(fShapes[i], *(fShapes[i]));
fShapes[i]->Reset();
}
return B_OK;
}
// Undo
status_t
FreezeTransformationCommand::Undo()
{
for (int32 i = 0; i < fCount; i++) {
if (!fShapes[i])
continue;
// restore original transformation
fShapes[i]->LoadFrom(&fOriginalTransformations[
i * Transformable::matrix_size]);
Transformable transform(*(fShapes[i]));
if (!transform.IsValid() || transform.IsIdentity())
continue;
transform.Invert();
_ApplyTransformation(fShapes[i], transform);
}
return B_OK;
}
// GetName
void
FreezeTransformationCommand::GetName(BString& name)
{
if (fCount > 1)
name << "Freeze Shapes";
else
name << "Freeze Shape";
}
// #pragma mark -
// _ApplyTransformation
void
FreezeTransformationCommand::_ApplyTransformation(Shape* shape,
const Transformable& transform)
{
// apply inverse of old shape transformation to every assigned path
int32 pathCount = shape->Paths()->CountPaths();
for (int32 i = 0; i < pathCount; i++) {
VectorPath* path = shape->Paths()->PathAtFast(i);
int32 shapes = 0;
int32 listeners = path->CountListeners();
for (int32 j = 0; j < listeners; j++) {
if (dynamic_cast<Shape*>(path->ListenerAtFast(j)))
shapes++;
}
// only freeze transformation of path if only one
// shape has it assigned
if (shapes == 1) {
path->ApplyTransform(transform);
} else {
printf("Not transfering transformation of \"%s\" onto "
"path \"%s\", because %ld other shapes "
"have it assigned.\n", shape->Name(), path->Name(),
shapes - 1);
}
}
// take care of style too
if (shape->Style() && shape->Style()->Gradient()) {
// TODO: not if more than one shape have this style assigned!
shape->Style()->Gradient()->Multiply(transform);
}
}
@@ -0,0 +1,40 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef FREEZE_TRANSFORMATION_COMMAND_H
#define FREEZE_TRANSFORMATION_COMMAND_H
#include "Command.h"
class Shape;
class Transformable;
class FreezeTransformationCommand : public Command {
public:
FreezeTransformationCommand(
Shape** const shapes,
int32 count);
virtual ~FreezeTransformationCommand();
virtual status_t InitCheck();
virtual status_t Perform();
virtual status_t Undo();
virtual void GetName(BString& name);
private:
void _ApplyTransformation(Shape* shape,
const Transformable& transform);
Shape** fShapes;
double* fOriginalTransformations;
int32 fCount;
};
#endif // FREEZE_TRANSFORMATION_COMMAND_H
@@ -1,3 +1,11 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "ReversePathCommand.h"
#include <stdio.h>
@@ -1,3 +1,11 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef REVERSE_PATH_COMMAND_H
#define REVERSE_PATH_COMMAND_H
@@ -0,0 +1,154 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "SplitPointsCommand.h"
#include <new>
#include <stdio.h>
#include "VectorPath.h"
using std::nothrow;
// constructor
// * when hitting the Delete key, so the selected points are the
// same as the ones to be removed
SplitPointsCommand::SplitPointsCommand(VectorPath* path,
const int32* indices,
int32 count)
: PathCommand(path),
fIndex(NULL),
fPoint(NULL),
fPointIn(NULL),
fPointOut(NULL),
fConnected(NULL),
fCount(0)
{
if (indices && count > 0) {
fIndex = new (nothrow) int32[count];
fPoint = new (nothrow) BPoint[count];
fPointIn = new (nothrow) BPoint[count];
fPointOut = new (nothrow) BPoint[count];
fConnected = new (nothrow) bool[count];
fCount = count;
}
if (InitCheck() < B_OK)
return;
memcpy(fIndex, indices, count * sizeof(int32));
for (int32 i = 0; i < count; i++) {
if (!fPath->GetPointsAt(fIndex[i],
fPoint[i],
fPointIn[i],
fPointOut[i],
&fConnected[i])) {
fPath = NULL;
break;
}
}
}
// destructor
SplitPointsCommand::~SplitPointsCommand()
{
delete[] fIndex;
delete[] fPoint;
delete[] fPointIn;
delete[] fPointOut;
delete[] fConnected;
}
// InitCheck
status_t
SplitPointsCommand::InitCheck()
{
status_t status = PathCommand::InitCheck();
if (status < B_OK)
return status;
if (!fIndex || !fPoint || !fPointIn || !fPointOut || !fConnected)
status = B_NO_MEMORY;
return status;
}
// Perform
status_t
SplitPointsCommand::Perform()
{
status_t status = B_OK;
AutoNotificationSuspender _(fPath);
// NOTE: fCount guaranteed > 0
// add points again at their respective index
for (int32 i = 0; i < fCount; i++) {
int32 index = fIndex[i] + 1 + i;
// "+ 1" to insert behind existing point
// "+ i" to adjust for already inserted points
if (fPath->AddPoint(fPoint[i], index)) {
fPath->SetPoint(index - 1,
fPoint[i],
fPointIn[i],
fPoint[i],
true);
fPath->SetPoint(index,
fPoint[i],
fPoint[i],
fPointOut[i],
true);
} else {
status = B_ERROR;
break;
}
}
return status;
}
// Undo
status_t
SplitPointsCommand::Undo()
{
status_t status = B_OK;
AutoNotificationSuspender _(fPath);
// remove inserted points and reset modified
// points to previous condition
for (int32 i = 0; i < fCount; i++) {
int32 index = fIndex[i] + 1;
if (fPath->RemovePoint(index)) {
fPath->SetPoint(index - 1,
fPoint[i],
fPointIn[i],
fPointOut[i],
fConnected[i]);
} else {
status = B_ERROR;
break;
}
}
if (status >= B_OK) {
// restore selection
_Select(fIndex, fCount);
}
return status;
}
// GetName
void
SplitPointsCommand::GetName(BString& name)
{
if (fCount > 1)
name << "Split Control Points";
else
name << "Split Control Point";
}
@@ -0,0 +1,39 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef SPLIT_POINTS_COMMAND_H
#define SPLIT_POINTS_COMMAND_H
#include "PathCommand.h"
class BPoint;
class SplitPointsCommand : public PathCommand {
public:
SplitPointsCommand(VectorPath* path,
const int32* indices,
int32 count);
virtual ~SplitPointsCommand();
virtual status_t InitCheck();
virtual status_t Perform();
virtual status_t Undo();
virtual void GetName(BString& name);
private:
int32* fIndex;
BPoint* fPoint;
BPoint* fPointIn;
BPoint* fPointOut;
bool* fConnected;
int32 fCount;
};
#endif // SPLIT_POINTS_COMMAND_H
@@ -0,0 +1,122 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "TransformPointsCommand.h"
#include <new>
#include <stdio.h>
#include "ChannelTransform.h"
#include "VectorPath.h"
// constructor
TransformPointsCommand::TransformPointsCommand(
TransformBox* box,
VectorPath* path,
const int32* indices,
const control_point* points,
int32 count,
BPoint pivot,
BPoint translation,
double rotation,
double xScale,
double yScale,
const char* name,
int32 nameIndex)
: TransformCommand(pivot,
translation,
rotation,
xScale,
yScale,
name,
nameIndex),
fTransformBox(box),
fPath(path),
fIndices(indices && count > 0 ?
new (nothrow) int32[count] : NULL),
fPoints(points && count > 0 ?
new (nothrow) control_point[count] : NULL),
fCount(count)
{
if (!fIndices || !fPoints)
return;
memcpy(fIndices, indices, fCount * sizeof(int32));
memcpy(fPoints, points, fCount * sizeof(control_point));
if (fTransformBox)
fTransformBox->AddListener(this);
}
// destructor
TransformPointsCommand::~TransformPointsCommand()
{
if (fTransformBox)
fTransformBox->RemoveListener(this);
delete[] fIndices;
delete[] fPoints;
}
// InitCheck
status_t
TransformPointsCommand::InitCheck()
{
return fPath && fIndices && fPoints ? TransformCommand::InitCheck()
: B_NO_INIT;
}
// #pragma mark -
// TransformBoxDeleted
void
TransformPointsCommand::TransformBoxDeleted(
const TransformBox* box)
{
if (fTransformBox == box) {
if (fTransformBox)
fTransformBox->RemoveListener(this);
fTransformBox = NULL;
}
}
// #pragma mark -
// _SetTransformation
status_t
TransformPointsCommand::_SetTransformation(
BPoint pivot, BPoint translation,
double rotation,
double xScale, double yScale) const
{
if (fTransformBox) {
fTransformBox->SetTransformation(pivot, translation,
rotation, xScale, yScale);
return B_OK;
}
ChannelTransform transform;
transform.SetTransformation(pivot, translation,
rotation, xScale, yScale);
// restore original points and apply transformation
for (int32 i = 0; i < fCount; i++) {
BPoint point = transform.Transform(fPoints[i].point);
BPoint pointIn = transform.Transform(fPoints[i].point_in);
BPoint pointOut = transform.Transform(fPoints[i].point_out);
if (!fPath->SetPoint(fIndices[i], point, pointIn, pointOut,
fPoints[i].connected))
return B_ERROR;
}
return B_OK;
}
@@ -0,0 +1,63 @@
/*
* Copyright 2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef TRANSFORM_POINTS_COMMAND_H
#define TRANSFORM_POINTS_COMMAND_H
#include "TransformBox.h"
#include "TransformCommand.h"
class Transformable;
class VectorPath;
struct control_point;
class TransformPointsCommand : public TransformCommand,
public TransformBoxListener {
public:
TransformPointsCommand(
TransformBox* box,
VectorPath* path,
const int32* indices,
const control_point* points,
int32 count,
BPoint pivot,
BPoint translation,
double rotation,
double xScale,
double yScale,
const char* name,
int32 nameIndex);
virtual ~TransformPointsCommand();
// Command interface
virtual status_t InitCheck();
// TransformBoxListener interface
virtual void TransformBoxDeleted(
const TransformBox* box);
protected:
// TransformCommand interface
virtual status_t _SetTransformation(BPoint pivotDiff,
BPoint translationDiff,
double rotationDiff,
double xScaleDiff,
double yScaleDiff) const;
TransformBox* fTransformBox;
VectorPath* fPath;
int32* fIndices;
control_point* fPoints;
int32 fCount;
};
#endif // TRANSFORM_POINTS_COMMAND_H
@@ -12,9 +12,8 @@
#include <stdio.h>
#include <string.h>
//#include "ExitTransformPointsCommand.h"
#include "StateView.h"
//#include "TransformPointsCommand.h"
#include "TransformPointsCommand.h"
#include "VectorPath.h"
using std::nothrow;
@@ -118,19 +117,20 @@ TransformCommand*
TransformPointsBox::MakeCommand(const char* commandName,
uint32 nameIndex)
{
return NULL;
// return new TransformPointsAction(fManipulator,
// fIndices,
// fPoints,
// fCount,
//// Translation(),
//// LocalRotation(),
//// LocalXScale(),
//// LocalYScale(),
// *this,
// CenterOffset(),
// actionName,
// nameIndex);
return new TransformPointsCommand(this, fPath,
fIndices,
fPoints,
fCount,
Pivot(),
Translation(),
LocalRotation(),
LocalXScale(),
LocalYScale(),
commandName,
nameIndex);
}
// #pragma mark -