Commit 9eccbb52 authored by twanvl's avatar twanvl

New rotation system (see forum thread).

Major changes:
  - when rotating, the top left corner of the rectangle stays in place.
  - ValueViewers get a dc that is pre-rotated/translated for them, i.e. (0,0) is the top-left of the viewer (with ValueViewer::getRotation).
  - moved 'angle' from individual Styles to the Style base class.
  - any rotation angle is now possible. angle is still an int for now.

This warrants a version bump
parent fe7d6b96
...@@ -94,9 +94,10 @@ intrusive_ptr<Field> read_new<Field>(Reader& reader) { ...@@ -94,9 +94,10 @@ intrusive_ptr<Field> read_new<Field>(Reader& reader) {
Style::Style(const FieldP& field) Style::Style(const FieldP& field)
: fieldP(field) : fieldP(field)
, z_index(0) , z_index(0)
, left(0), top(0) , left(-1), top(-1)
, width(0), height(0) , width(-1), height(-1)
, right(0), bottom(0) , right(-1), bottom(-1)
, angle(0)
, visible(true) , visible(true)
, automatic_side(AUTO_UNKNOWN) , automatic_side(AUTO_UNKNOWN)
, content_dependent(false) , content_dependent(false)
...@@ -112,6 +113,7 @@ IMPLEMENT_REFLECTION(Style) { ...@@ -112,6 +113,7 @@ IMPLEMENT_REFLECTION(Style) {
REFLECT(top); REFLECT(top);
REFLECT(height); REFLECT(height);
REFLECT(bottom); REFLECT(bottom);
REFLECT(angle);
REFLECT(visible); REFLECT(visible);
} }
...@@ -130,28 +132,44 @@ int Style::update(Context& ctx) { ...@@ -130,28 +132,44 @@ int Style::update(Context& ctx) {
| top .update(ctx) | top .update(ctx)
| height .update(ctx) | height .update(ctx)
| bottom .update(ctx) | bottom .update(ctx)
| angle .update(ctx)
| visible.update(ctx); | visible.update(ctx);
// determine automatic_side // determine automatic_side and attachment of rotation point
if (automatic_side == AUTO_UNKNOWN) { if (automatic_side == AUTO_UNKNOWN) {
if (right == 0) automatic_side = (AutomaticSide)(automatic_side | AUTO_RIGHT); if (right == -1) automatic_side = (AutomaticSide)(automatic_side | AUTO_RIGHT);
else if (width == 0) automatic_side = (AutomaticSide)(automatic_side | AUTO_WIDTH); else if (width == -1) automatic_side = (AutomaticSide)(automatic_side | AUTO_WIDTH);
else automatic_side = (AutomaticSide)(automatic_side | AUTO_LEFT); else if (left == -1) automatic_side = (AutomaticSide)(automatic_side | AUTO_LEFT);
if (bottom == 0) automatic_side = (AutomaticSide)(automatic_side | AUTO_BOTTOM); else automatic_side = (AutomaticSide)(automatic_side | AUTO_LR);
else if (height == 0) automatic_side = (AutomaticSide)(automatic_side | AUTO_HEIGHT); if (bottom == -1) automatic_side = (AutomaticSide)(automatic_side | AUTO_BOTTOM);
else automatic_side = (AutomaticSide)(automatic_side | AUTO_TOP); else if (height == -1) automatic_side = (AutomaticSide)(automatic_side | AUTO_HEIGHT);
} else if (top == -1) automatic_side = (AutomaticSide)(automatic_side | AUTO_TOP);
if (automatic_side & AUTO_WIDTH){ else automatic_side = (AutomaticSide)(automatic_side | AUTO_TB);
changed=changed;//BREAKPOINT changed = true;
} }
if (!changed) return CHANGE_NONE;
// update the automatic_side // update the automatic_side
if (automatic_side & AUTO_LEFT) left = right - width; if (automatic_side & AUTO_LEFT) left = right - width;
else if (automatic_side & AUTO_WIDTH) width = right - left; else if (automatic_side & AUTO_WIDTH) width = right - left;
else right = left + width; else if (automatic_side & AUTO_RIGHT) right = left + width;
else {int lr = left + right; left = (lr - width) / 2; right = (lr + width) / 2; }
if (automatic_side & AUTO_TOP) top = bottom - height; if (automatic_side & AUTO_TOP) top = bottom - height;
else if (automatic_side & AUTO_HEIGHT) height = bottom - top; else if (automatic_side & AUTO_HEIGHT) height = bottom - top;
else bottom = top + height; else if (automatic_side & AUTO_BOTTOM) bottom = top + height;
// are there changes? else {int tb = top + bottom; top = (tb - height) / 2; bottom = (tb + height) / 2; }
return changed; // adjust rotation point
if (angle != 0 && (automatic_side & (AUTO_LEFT | AUTO_TOP))) {
double s = sin(angle * M_PI / 180), c = cos(angle * M_PI / 180);
if (automatic_side & AUTO_LEFT) { // attach right corner instead of left
left = left + width * (1 - c);
top = top + width * s;
}
if (automatic_side & AUTO_TOP) { // attach botom corner instead of top
left = left - height * s;
top = top + height * (1 - c);
}
}
// done
return CHANGE_OTHER;
} }
void Style::initDependencies(Context& ctx, const Dependency& dep) const { void Style::initDependencies(Context& ctx, const Dependency& dep) const {
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <util/reflect.hpp> #include <util/reflect.hpp>
#include <util/alignment.hpp> #include <util/alignment.hpp>
#include <util/age.hpp> #include <util/age.hpp>
#include <util/rotation.hpp>
#include <script/scriptable.hpp> #include <script/scriptable.hpp>
#include <script/dependency.hpp> #include <script/dependency.hpp>
...@@ -98,17 +99,21 @@ class Style : public IntrusivePtrVirtualBase { ...@@ -98,17 +99,21 @@ class Style : public IntrusivePtrVirtualBase {
Scriptable<double> left, top; ///< Position of this field Scriptable<double> left, top; ///< Position of this field
Scriptable<double> width, height; ///< Position of this field Scriptable<double> width, height; ///< Position of this field
Scriptable<double> right, bottom; ///< Position of this field Scriptable<double> right, bottom; ///< Position of this field
Scriptable<int> angle; ///< Rotation of the box
Scriptable<bool> visible; ///< Is this field visible? Scriptable<bool> visible; ///< Is this field visible?
enum AutomaticSide { enum AutomaticSide {
AUTO_UNKNOWN = 0x00, AUTO_UNKNOWN = 0x00,
AUTO_LEFT = 0x01, AUTO_WIDTH = 0x02, AUTO_RIGHT = 0x04, AUTO_LEFT = 0x01, AUTO_WIDTH = 0x02, AUTO_RIGHT = 0x04, AUTO_LR = 0x08,
AUTO_TOP = 0x10, AUTO_HEIGHT = 0x20, AUTO_BOTTOM = 0x40, AUTO_TOP = 0x10, AUTO_HEIGHT = 0x20, AUTO_BOTTOM = 0x40, AUTO_TB = 0x80,
ATTACH_LEFT = 0x04, ATTACH_CENTER = 0x02, ATTACH_RIGHT = 0x01,
ATTACH_TOP = 0x40, ATTACH_MIDDLE = 0x20, ATTACH_BOTTOM = 0x10,
} automatic_side : 8; ///< Which of (left, width, right) and (top, height, bottom) is determined automatically? } automatic_side : 8; ///< Which of (left, width, right) and (top, height, bottom) is determined automatically?
bool content_dependent; ///< Does this style depend on content properties? bool content_dependent; ///< Does this style depend on content properties?
inline RealPoint getPos() const { return RealPoint(left, top ); } inline RealPoint getPos() const { return RealPoint(left, top ); }
inline RealSize getSize() const { return RealSize ( width, height); } inline RealSize getSize() const { return RealSize ( width, height); }
inline RealRect getRect() const { return RealRect (left, top, width, height); } inline RealRect getExternalRect() const { return RealRect (left, top, width, height); }
inline RealRect getInternalRect() const { return RealRect(0, 0, width, height); }
/// Get a copy of this style /// Get a copy of this style
virtual StyleP clone() const = 0; virtual StyleP clone() const = 0;
......
...@@ -178,7 +178,6 @@ ChoiceStyle::ChoiceStyle(const ChoiceFieldP& field) ...@@ -178,7 +178,6 @@ ChoiceStyle::ChoiceStyle(const ChoiceFieldP& field)
, choice_images_initialized(false) , choice_images_initialized(false)
, combine(COMBINE_NORMAL) , combine(COMBINE_NORMAL)
, alignment(ALIGN_STRETCH) , alignment(ALIGN_STRETCH)
, angle(0)
, thumbnails(nullptr) , thumbnails(nullptr)
{} {}
...@@ -291,7 +290,6 @@ IMPLEMENT_REFLECTION(ChoiceStyle) { ...@@ -291,7 +290,6 @@ IMPLEMENT_REFLECTION(ChoiceStyle) {
REFLECT_N("mask",mask_filename); REFLECT_N("mask",mask_filename);
REFLECT(combine); REFLECT(combine);
REFLECT(alignment); REFLECT(alignment);
REFLECT(angle);
REFLECT(font); REFLECT(font);
REFLECT(image); REFLECT(image);
REFLECT(choice_images); REFLECT(choice_images);
......
...@@ -153,7 +153,6 @@ class ChoiceStyle : public Style { ...@@ -153,7 +153,6 @@ class ChoiceStyle : public Style {
ImageCombine combine; ///< Combining mode for drawing the images ImageCombine combine; ///< Combining mode for drawing the images
Alignment alignment; ///< Alignment of images Alignment alignment; ///< Alignment of images
Image mask; ///< The actual mask image Image mask; ///< The actual mask image
int angle; ///< Angle by which the images are rotated
wxImageList* thumbnails; ///< Thumbnails for the choices wxImageList* thumbnails; ///< Thumbnails for the choices
vector<ThumbnailStatus> thumbnails_status; ///< Which thumbnails are up to date? vector<ThumbnailStatus> thumbnails_status; ///< Which thumbnails are up to date?
// information from image rendering // information from image rendering
......
...@@ -25,14 +25,12 @@ IMPLEMENT_REFLECTION(ImageField) { ...@@ -25,14 +25,12 @@ IMPLEMENT_REFLECTION(ImageField) {
IMPLEMENT_REFLECTION(ImageStyle) { IMPLEMENT_REFLECTION(ImageStyle) {
REFLECT_BASE(Style); REFLECT_BASE(Style);
REFLECT(angle);
REFLECT_N("mask", mask_filename); REFLECT_N("mask", mask_filename);
REFLECT_N("default", default_image); REFLECT_N("default", default_image);
} }
int ImageStyle::update(Context& ctx) { int ImageStyle::update(Context& ctx) {
return Style ::update(ctx) return Style ::update(ctx)
| angle .update(ctx) * CHANGE_OTHER
| mask_filename.update(ctx) * CHANGE_MASK | mask_filename.update(ctx) * CHANGE_MASK
| default_image.update(ctx) * CHANGE_DEFAULT; | default_image.update(ctx) * CHANGE_DEFAULT;
} }
......
...@@ -38,7 +38,6 @@ class ImageStyle : public Style { ...@@ -38,7 +38,6 @@ class ImageStyle : public Style {
inline ImageStyle(const ImageFieldP& field) : Style(field) {} inline ImageStyle(const ImageFieldP& field) : Style(field) {}
DECLARE_STYLE_TYPE(Image); DECLARE_STYLE_TYPE(Image);
Scriptable<int> angle; ///< Rotation of images
Scriptable<String> mask_filename; ///< Filename for a mask image Scriptable<String> mask_filename; ///< Filename for a mask image
ScriptableImage default_image; ///< Placeholder ScriptableImage default_image; ///< Placeholder
......
...@@ -44,7 +44,6 @@ TextStyle::TextStyle(const TextFieldP& field) ...@@ -44,7 +44,6 @@ TextStyle::TextStyle(const TextFieldP& field)
: Style(field) : Style(field)
, always_symbol(false), allow_formating(true) , always_symbol(false), allow_formating(true)
, alignment(ALIGN_TOP_LEFT) , alignment(ALIGN_TOP_LEFT)
, angle(0)
, padding_left (0), padding_left_min (10000) , padding_left (0), padding_left_min (10000)
, padding_right (0), padding_right_min (10000) , padding_right (0), padding_right_min (10000)
, padding_top (0), padding_top_min (10000) , padding_top (0), padding_top_min (10000)
...@@ -73,8 +72,7 @@ int TextStyle::update(Context& ctx) { ...@@ -73,8 +72,7 @@ int TextStyle::update(Context& ctx) {
return Style ::update(ctx) return Style ::update(ctx)
| font .update(ctx) * CHANGE_OTHER | font .update(ctx) * CHANGE_OTHER
| symbol_font.update(ctx) * CHANGE_OTHER | symbol_font.update(ctx) * CHANGE_OTHER
| alignment .update(ctx) * CHANGE_OTHER | alignment .update(ctx) * CHANGE_OTHER;
| angle .update(ctx) * CHANGE_OTHER;
} }
void TextStyle::initDependencies(Context& ctx, const Dependency& dep) const { void TextStyle::initDependencies(Context& ctx, const Dependency& dep) const {
Style ::initDependencies(ctx, dep); Style ::initDependencies(ctx, dep);
...@@ -100,7 +98,6 @@ IMPLEMENT_REFLECTION(TextStyle) { ...@@ -100,7 +98,6 @@ IMPLEMENT_REFLECTION(TextStyle) {
REFLECT(always_symbol); REFLECT(always_symbol);
REFLECT(allow_formating); REFLECT(allow_formating);
REFLECT(alignment); REFLECT(alignment);
REFLECT(angle);
REFLECT(padding_left); REFLECT(padding_left);
REFLECT(padding_right); REFLECT(padding_right);
REFLECT(padding_top); REFLECT(padding_top);
......
...@@ -35,6 +35,8 @@ class TextField : public Field { ...@@ -35,6 +35,8 @@ class TextField : public Field {
OptionalScript script; ///< Script to apply to all values OptionalScript script; ///< Script to apply to all values
OptionalScript default_script; ///< Script that generates the default value OptionalScript default_script; ///< Script that generates the default value
//%OptionalScript view_script; ///< Script to apply before viewing
//%OptionalScript unview_script; ///< Script to apply after changes to the view
bool multi_line; ///< Are newlines allowed in the text? bool multi_line; ///< Are newlines allowed in the text?
String default_name; ///< Name of "default" value String default_name; ///< Name of "default" value
...@@ -57,7 +59,6 @@ class TextStyle : public Style { ...@@ -57,7 +59,6 @@ class TextStyle : public Style {
bool always_symbol; ///< Should everything be drawn as symbols? bool always_symbol; ///< Should everything be drawn as symbols?
bool allow_formating; ///< Is formating (bold/italic/..) allowed? bool allow_formating; ///< Is formating (bold/italic/..) allowed?
Scriptable<Alignment> alignment; ///< Alignment inside the box Scriptable<Alignment> alignment; ///< Alignment inside the box
Scriptable<int> angle; ///< Angle of the text inside the box
double padding_left, padding_left_min; ///< Padding double padding_left, padding_left_min; ///< Padding
double padding_right, padding_right_min; ///< Padding double padding_right, padding_right_min; ///< Padding
double padding_top, padding_top_min; ///< Padding double padding_top, padding_top_min; ///< Padding
...@@ -80,14 +81,6 @@ class TextStyle : public Style { ...@@ -80,14 +81,6 @@ class TextStyle : public Style {
virtual void initDependencies(Context&, const Dependency&) const; virtual void initDependencies(Context&, const Dependency&) const;
virtual void checkContentDependencies(Context&, const Dependency&) const; virtual void checkContentDependencies(Context&, const Dependency&) const;
/// The rotation to use when drawing
inline Rotation getRotation() const {
return Rotation(angle, getRect(), 1.0, getStretch());
}
/// The rotation to use when determining content layout, does not include the stretch factor
inline Rotation getRotationNoStretch() const {
return Rotation(angle, getRect());
}
/// Stretch factor to use /// Stretch factor to use
double getStretch() const; double getStretch() const;
......
...@@ -34,7 +34,7 @@ Rotation UnzoomedDataViewer::getRotation() const { ...@@ -34,7 +34,7 @@ Rotation UnzoomedDataViewer::getRotation() const {
return DataViewer::getRotation(); return DataViewer::getRotation();
} else { } else {
if (!stylesheet) stylesheet = set->stylesheet; if (!stylesheet) stylesheet = set->stylesheet;
return Rotation(0, stylesheet->getCardRect(), 1.0, 1.0, true); return Rotation(0, stylesheet->getCardRect(), 1.0, 1.0, ROTATION_ATTACH_TOP_LEFT);
} }
} }
......
...@@ -32,6 +32,7 @@ class StatsDimension : public IntrusivePtrBase<StatsDimension> { ...@@ -32,6 +32,7 @@ class StatsDimension : public IntrusivePtrBase<StatsDimension> {
String description; ///< Description, used in status bar String description; ///< Description, used in status bar
int position_hint; ///< Hint for the ordering int position_hint; ///< Hint for the ordering
String icon_filename; ///< Icon for lists String icon_filename; ///< Icon for lists
Bitmap icon; ///< The loaded icon (optional of course)
OptionalScript script; ///< Script that determines the value(s) OptionalScript script; ///< Script that determines the value(s)
bool numeric; ///< Are the values numeric? If so, they require special sorting bool numeric; ///< Are the values numeric? If so, they require special sorting
bool show_empty; ///< Should "" be shown? bool show_empty; ///< Should "" be shown?
......
...@@ -302,7 +302,7 @@ void SymbolFont::drawWithText(RotatedDC& dc, const RealRect& rect, double font_s ...@@ -302,7 +302,7 @@ void SymbolFont::drawWithText(RotatedDC& dc, const RealRect& rect, double font_s
if (def) { if (def) {
Bitmap bmp = def->getBitmap(*this, dc.trS(font_size)); Bitmap bmp = def->getBitmap(*this, dc.trS(font_size));
// align symbol // align symbol
sym_rect.size() = dc.trInvS(RealSize(bmp.GetWidth(), bmp.GetHeight())); sym_rect.size() = dc.trInvS(RealSize(bmp));
sym_rect.position() = align_in_rect(align, sym_rect.size(), rect); sym_rect.position() = align_in_rect(align, sym_rect.size(), rect);
// draw // draw
dc.DrawBitmap(bmp, sym_rect.position()); dc.DrawBitmap(bmp, sym_rect.position());
......
...@@ -75,6 +75,8 @@ Image conform_image(const Image& img, const GeneratedImage::Options& options) { ...@@ -75,6 +75,8 @@ Image conform_image(const Image& img, const GeneratedImage::Options& options) {
if (options.saturate) { if (options.saturate) {
saturate(image, 40); saturate(image, 40);
} }
options.width = image.GetWidth();
options.height = image.GetHeight();
// rotate? // rotate?
if (options.angle != 0) { if (options.angle != 0) {
image = rotate_image(image, options.angle); image = rotate_image(image, options.angle);
......
...@@ -33,7 +33,8 @@ class GeneratedImage : public ScriptValue { ...@@ -33,7 +33,8 @@ class GeneratedImage : public ScriptValue {
, package(package), local_package(local_package) , package(package), local_package(local_package)
{} {}
int width, height; ///< Width to force the image to, or 0 to keep the width of the input mutable int width, height; ///< Width to force the image to, or 0 to keep the width of the input
///< In that case, width and height will be later set to the actual size
double zoom; ///< Zoom factor to use, when witdth=height=0 double zoom; ///< Zoom factor to use, when witdth=height=0
int angle; ///< Angle to rotate image by afterwards int angle; ///< Angle to rotate image by afterwards
PreserveAspect preserve_aspect; PreserveAspect preserve_aspect;
......
...@@ -45,20 +45,30 @@ void sharp_resample_and_clip(const Image& img_in, Image& img_out, wxRect rect, i ...@@ -45,20 +45,30 @@ void sharp_resample_and_clip(const Image& img_in, Image& img_out, wxRect rect, i
/// Draw text by first drawing it using a larger font and then downsampling it /// Draw text by first drawing it using a larger font and then downsampling it
/** optionally rotated by an angle. /** optionally rotated by an angle.
* rect = rectangle to draw in * pos = the position to draw
* rect = rectangle to draw in (a rectangle somewhere around pos)
* stretch = amount to stretch in the direction of the text after drawing * stretch = amount to stretch in the direction of the text after drawing
* (wc,hc) = the corner where drawing should begin, (0,0) for top-left, (1,1) for bottom-right
*/ */
void draw_resampled_text(DC& dc, const RealRect& rect, double stretch, int wc, int hc, int angle, const String& text, int blur_radius = 0, int repeat = 1); void draw_resampled_text(DC& dc, const RealPoint& pos, const RealRect& rect, double stretch, int angle, const String& text, int blur_radius = 0, int repeat = 1);
// scaling factor to use when drawing resampled text // scaling factor to use when drawing resampled text
extern const int text_scaling; extern const int text_scaling;
// ----------------------------------------------------------------------------- : Image rotation // ----------------------------------------------------------------------------- : Image rotation
/// Is an angle a {0,90,180,270}?
inline bool straight(int angle) { return angle % 90 == 0; }
/// Is an angle sideways (90 or 270 degrees)? /// Is an angle sideways (90 or 270 degrees)?
// Note: angle & 2 == 0 for angle in {0, 180} and != 0 for angle in {90, 270) inline bool sideways(int angle) {
inline bool sideways(int angle) { return (angle & 2) != 0; } int a = (angle + 3600) % 180;
return (a > 45 && a < 135);
}
/// Convert radians to degrees
inline double rad_to_deg(double rad) { return rad * (180.0 / M_PI); }
/// Convert degrees to radians
inline double deg_to_rad(double deg) { return deg * (M_PI / 180.0); }
/// Rotates an image counter clockwise /// Rotates an image counter clockwise
/// angle must be a multiple of 90, i.e. {0,90,180,270} /// angle must be a multiple of 90, i.e. {0,90,180,270}
......
...@@ -145,14 +145,14 @@ void blur_image_alpha(Image& img) { ...@@ -145,14 +145,14 @@ void blur_image_alpha(Image& img) {
// Draw text by first drawing it using a larger font and then downsampling it // Draw text by first drawing it using a larger font and then downsampling it
// optionally rotated by an angle // optionally rotated by an angle
// (w2,h2) = size of text void draw_resampled_text(DC& dc, const RealPoint& pos, const RealRect& rect, double stretch, int angle, const String& text, int blur_radius, int repeat) {
// (wc,hc) = the corner where drawing should begin, (0,0) for top-left, (1,1) for bottom-right
void draw_resampled_text(DC& dc, const RealRect& rect, double stretch, int wc, int hc, int angle, const String& text, int blur_radius, int repeat) {
// enlarge slightly; some fonts are larger then the GetTextExtent tells us (especially italic fonts) // enlarge slightly; some fonts are larger then the GetTextExtent tells us (especially italic fonts)
int w = static_cast<int>(rect.width) + 3 + 2 * blur_radius, h = static_cast<int>(rect.height) + 1 + 2 * blur_radius; int w = static_cast<int>(rect.width) + 3 + 2 * blur_radius, h = static_cast<int>(rect.height) + 1 + 2 * blur_radius;
// determine sub-pixel position // determine sub-pixel position
int xi = static_cast<int>(rect.x), yi = static_cast<int>(rect.y); int xi = static_cast<int>(rect.x) - blur_radius / text_scaling,
int xsub = static_cast<int>(text_scaling * (rect.x - xi)), ysub = static_cast<int>(text_scaling * (rect.y - yi)); yi = static_cast<int>(rect.y) - blur_radius / text_scaling;
int xsub = static_cast<int>(text_scaling * (pos.x - xi)),
ysub = static_cast<int>(text_scaling * (pos.y - yi));
// draw text // draw text
Bitmap buffer(w * text_scaling, h * text_scaling, 24); // should be initialized to black Bitmap buffer(w * text_scaling, h * text_scaling, 24); // should be initialized to black
wxMemoryDC mdc; wxMemoryDC mdc;
...@@ -161,7 +161,7 @@ void draw_resampled_text(DC& dc, const RealRect& rect, double stretch, int wc, i ...@@ -161,7 +161,7 @@ void draw_resampled_text(DC& dc, const RealRect& rect, double stretch, int wc, i
// now draw the text // now draw the text
mdc.SetFont(dc.GetFont()); mdc.SetFont(dc.GetFont());
mdc.SetTextForeground(*wxWHITE); mdc.SetTextForeground(*wxWHITE);
mdc.DrawRotatedText(text, (wc * w + blur_radius) * text_scaling + xsub, (hc * h + blur_radius) * text_scaling + ysub, angle); mdc.DrawRotatedText(text, xsub, ysub, angle);
// get image // get image
mdc.SelectObject(wxNullBitmap); mdc.SelectObject(wxNullBitmap);
Image img_large = buffer.ConvertToImage(); Image img_large = buffer.ConvertToImage();
...@@ -177,8 +177,7 @@ void draw_resampled_text(DC& dc, const RealRect& rect, double stretch, int wc, i ...@@ -177,8 +177,7 @@ void draw_resampled_text(DC& dc, const RealRect& rect, double stretch, int wc, i
} }
// step 3. draw to dc // step 3. draw to dc
for (int i = 0 ; i < repeat ; ++i) { for (int i = 0 ; i < repeat ; ++i) {
dc.DrawBitmap(img_small, xi + static_cast<int>(wc * (rect.width - w)) - blur_radius, dc.DrawBitmap(img_small, xi, yi);
yi + static_cast<int>(hc * (rect.height - h)) - blur_radius);
} }
} }
...@@ -84,10 +84,13 @@ struct Rotate270 { ...@@ -84,10 +84,13 @@ struct Rotate270 {
Image rotate_image(const Image& image, int angle) { Image rotate_image(const Image& image, int angle) {
switch (angle % 360) { switch (angle % 360) {
case 0: return image;
case 90: return rotate_image_impl<Rotate90> (image); case 90: return rotate_image_impl<Rotate90> (image);
case 180: return rotate_image_impl<Rotate180>(image); case 180: return rotate_image_impl<Rotate180>(image);
case 270: return rotate_image_impl<Rotate270>(image); case 270: return rotate_image_impl<Rotate270>(image);
default: return image; default:
if (!image.HasAlpha()) const_cast<Image&>(image).InitAlpha();
return image.Rotate(angle * M_PI / 180, wxPoint(0,0));
} }
} }
......
...@@ -210,15 +210,19 @@ void DataEditor::onLeftDown(wxMouseEvent& ev) { ...@@ -210,15 +210,19 @@ void DataEditor::onLeftDown(wxMouseEvent& ev) {
} }
void DataEditor::onLeftUp(wxMouseEvent& ev) { void DataEditor::onLeftUp(wxMouseEvent& ev) {
if (HasCapture()) ReleaseMouse(); if (HasCapture()) ReleaseMouse();
RealPoint pos = mousePoint(ev); if (current_editor && current_viewer) {
if (current_editor && current_viewer && current_viewer->containsPoint(pos)) { RealPoint pos = mousePoint(ev, *current_viewer);
current_editor->onLeftUp(pos, ev); if (current_viewer->containsPoint(pos)) {
current_editor->onLeftUp(pos, ev);
}
} }
} }
void DataEditor::onLeftDClick(wxMouseEvent& ev) { void DataEditor::onLeftDClick(wxMouseEvent& ev) {
RealPoint pos = mousePoint(ev); if (current_editor && current_viewer) {
if (current_editor && current_viewer && current_viewer->containsPoint(pos)) { RealPoint pos = mousePoint(ev, *current_viewer);
current_editor->onLeftDClick(pos, ev); if (current_viewer->containsPoint(pos)) {
current_editor->onLeftDClick(pos, ev);
}
} }
} }
void DataEditor::onRightDown(wxMouseEvent& ev) { void DataEditor::onRightDown(wxMouseEvent& ev) {
...@@ -227,22 +231,25 @@ void DataEditor::onRightDown(wxMouseEvent& ev) { ...@@ -227,22 +231,25 @@ void DataEditor::onRightDown(wxMouseEvent& ev) {
selectField(ev, &ValueEditor::onRightDown); selectField(ev, &ValueEditor::onRightDown);
} }
void DataEditor::onMouseWheel(wxMouseEvent& ev) { void DataEditor::onMouseWheel(wxMouseEvent& ev) {
RealPoint pos = mousePoint(ev); if (current_editor && current_viewer) {
if (current_editor && current_viewer && current_viewer->containsPoint(pos)) { RealPoint pos = mousePoint(ev, *current_viewer);
if (current_editor->onMouseWheel(pos, ev)) return; if (current_viewer->containsPoint(pos)) {
if (current_editor->onMouseWheel(pos, ev)) return;
}
} }
ev.Skip(); ev.Skip();
} }
void DataEditor::onMotion(wxMouseEvent& ev) { void DataEditor::onMotion(wxMouseEvent& ev) {
RealPoint pos = mousePoint(ev); if (current_editor && current_viewer) {
if (current_editor && ev.LeftIsDown()) { RealPoint pos = mousePoint(ev, *current_viewer);
current_editor->onMotion(pos, ev); current_editor->onMotion(pos, ev);
} }
if (!HasCapture()) { if (!HasCapture()) {
// find editor under mouse // find editor under mouse
ValueViewer* new_hovered_viewer = nullptr; ValueViewer* new_hovered_viewer = nullptr;
FOR_EACH_REVERSE(v,viewers) { // find high z index fields first FOR_EACH_REVERSE(v,viewers) { // find high z index fields first
RealPoint pos = mousePoint(ev, *v);
if (v->containsPoint(pos) && v->getField()->editable) { if (v->containsPoint(pos) && v->getField()->editable) {
new_hovered_viewer = v.get(); new_hovered_viewer = v.get();
break; break;
...@@ -250,6 +257,7 @@ void DataEditor::onMotion(wxMouseEvent& ev) { ...@@ -250,6 +257,7 @@ void DataEditor::onMotion(wxMouseEvent& ev) {
} }
if (hovered_viewer && hovered_viewer != new_hovered_viewer) { if (hovered_viewer && hovered_viewer != new_hovered_viewer) {
ValueEditor* e = hovered_viewer->getEditor(); ValueEditor* e = hovered_viewer->getEditor();
RealPoint pos = mousePoint(ev, *hovered_viewer);
if (e) e->onMouseLeave(pos, ev); if (e) e->onMouseLeave(pos, ev);
} }
hovered_viewer = new_hovered_viewer; hovered_viewer = new_hovered_viewer;
...@@ -257,6 +265,7 @@ void DataEditor::onMotion(wxMouseEvent& ev) { ...@@ -257,6 +265,7 @@ void DataEditor::onMotion(wxMouseEvent& ev) {
wxFrame* frame = dynamic_cast<wxFrame*>( wxGetTopLevelParent(this) ); wxFrame* frame = dynamic_cast<wxFrame*>( wxGetTopLevelParent(this) );
if (hovered_viewer) { if (hovered_viewer) {
ValueEditor* e = hovered_viewer->getEditor(); ValueEditor* e = hovered_viewer->getEditor();
RealPoint pos = mousePoint(ev, *hovered_viewer);
wxCursor c; wxCursor c;
if (e) c = e->cursor(pos); if (e) c = e->cursor(pos);
if (c.Ok()) SetCursor(c); if (c.Ok()) SetCursor(c);
...@@ -274,7 +283,7 @@ void DataEditor::onMouseLeave(wxMouseEvent& ev) { ...@@ -274,7 +283,7 @@ void DataEditor::onMouseLeave(wxMouseEvent& ev) {
// on mouse leave for editor // on mouse leave for editor
if (hovered_viewer) { if (hovered_viewer) {
ValueEditor* e = hovered_viewer->getEditor(); ValueEditor* e = hovered_viewer->getEditor();
if (e) e->onMouseLeave(mousePoint(ev), ev); if (e) e->onMouseLeave(mousePoint(ev,*hovered_viewer), ev);
hovered_viewer = nullptr; hovered_viewer = nullptr;
} }
// clear status text // clear status text
...@@ -283,18 +292,20 @@ void DataEditor::onMouseLeave(wxMouseEvent& ev) { ...@@ -283,18 +292,20 @@ void DataEditor::onMouseLeave(wxMouseEvent& ev) {
} }
void DataEditor::selectField(wxMouseEvent& ev, bool (ValueEditor::*event)(const RealPoint&, wxMouseEvent&)) { void DataEditor::selectField(wxMouseEvent& ev, bool (ValueEditor::*event)(const RealPoint&, wxMouseEvent&)) {
RealPoint pos = mousePoint(ev);
// change viewer/editor // change viewer/editor
ValueEditor* old_editor = current_editor; ValueEditor* old_editor = current_editor;
selectFieldNoEvents(pos); selectFieldNoEvents(ev);
if (old_editor != current_editor) { if (old_editor != current_editor) {
// selection has changed, send focus events // selection has changed, send focus events
if (old_editor) old_editor->onLoseFocus(); if (old_editor) old_editor->onLoseFocus();
if (current_editor) current_editor->onFocus(); if (current_editor) current_editor->onFocus();
} }
// pass event // pass event
if (current_editor && current_viewer && current_viewer->containsPoint(pos)) { if (current_editor && current_viewer) {
(current_editor->*event)(pos, ev); RealPoint pos = mousePoint(ev, *current_viewer);
if (current_viewer->containsPoint(pos)) {
(current_editor->*event)(pos, ev);
}
} }
// refresh? // refresh?
if (old_editor != current_editor) { if (old_editor != current_editor) {
...@@ -303,10 +314,10 @@ void DataEditor::selectField(wxMouseEvent& ev, bool (ValueEditor::*event)(const ...@@ -303,10 +314,10 @@ void DataEditor::selectField(wxMouseEvent& ev, bool (ValueEditor::*event)(const
onChange(); onChange();
} }
} }
void DataEditor::selectFieldNoEvents(const RealPoint& p) { void DataEditor::selectFieldNoEvents(const wxMouseEvent& ev) {
FOR_EACH_EDITOR_REVERSE { // find high z index fields first FOR_EACH_EDITOR_REVERSE { // find high z index fields first
if (v->getField()->editable && (v->containsPoint(p) || if (v->getField()->editable && (v->containsPoint(mousePoint(ev,*v)) ||
(nativeLook() && p.y >= v->getStyle()->top && p.y < v->getStyle()->bottom) )) { (nativeLook() && ev.GetY() >= v->getStyle()->top && ev.GetY() < v->getStyle()->bottom) )) {
current_viewer = v.get(); current_viewer = v.get();
current_editor = e; current_editor = e;
return; return;
...@@ -314,8 +325,10 @@ void DataEditor::selectFieldNoEvents(const RealPoint& p) { ...@@ -314,8 +325,10 @@ void DataEditor::selectFieldNoEvents(const RealPoint& p) {
} }
} }
RealPoint DataEditor::mousePoint(const wxMouseEvent& ev) { RealPoint DataEditor::mousePoint(const wxMouseEvent& ev, const ValueViewer& viewer) {
return getRotation().trInv(RealPoint(ev.GetX(), ev.GetY())); Rotation rot = getRotation();
Rotater r(rot,viewer.getRotation());
return rot.trInv(RealPoint(ev.GetX(), ev.GetY()));
} }
// ----------------------------------------------------------------------------- : Keyboard events // ----------------------------------------------------------------------------- : Keyboard events
......
...@@ -118,9 +118,9 @@ class DataEditor : public CardViewer { ...@@ -118,9 +118,9 @@ class DataEditor : public CardViewer {
/** Sends an event to the event function of the current viewer */ /** Sends an event to the event function of the current viewer */
void selectField(wxMouseEvent& ev, bool (ValueEditor::*event)(const RealPoint&, wxMouseEvent&)); void selectField(wxMouseEvent& ev, bool (ValueEditor::*event)(const RealPoint&, wxMouseEvent&));
// selectField, but don't send events // selectField, but don't send events
void selectFieldNoEvents(const RealPoint& pos); void selectFieldNoEvents(const wxMouseEvent&);
/// Convert mouse coordinates to internal coordinates /// Convert mouse coordinates to internal coordinates
RealPoint mousePoint(const wxMouseEvent&); RealPoint mousePoint(const wxMouseEvent&, const ValueViewer& viewer);
// Create tab index ordering of the (editable) viewers // Create tab index ordering of the (editable) viewers
void createTabIndex(); void createTabIndex();
......
...@@ -38,7 +38,7 @@ wxSize CardViewer::DoGetBestSize() const { ...@@ -38,7 +38,7 @@ wxSize CardViewer::DoGetBestSize() const {
void CardViewer::redraw(const ValueViewer& v) { void CardViewer::redraw(const ValueViewer& v) {
if (drawing) return; if (drawing) return;
up_to_date = false; up_to_date = false;
RefreshRect(getRotation().tr(v.boundingBox()), false); RefreshRect(getRotation().trRectToBB(v.boundingBox()), false);
} }
void CardViewer::onChange() { void CardViewer::onChange() {
...@@ -98,7 +98,7 @@ bool CardViewer::shouldDraw(const ValueViewer& v) const { ...@@ -98,7 +98,7 @@ bool CardViewer::shouldDraw(const ValueViewer& v) const {
// int dx = GetScrollPos(wxHORIZONTAL), dy = GetScrollPos(wxVERTICAL); // int dx = GetScrollPos(wxHORIZONTAL), dy = GetScrollPos(wxVERTICAL);
// wxRegion clip = GetUpdateRegion(); // wxRegion clip = GetUpdateRegion();
// clip.Offset(dx, dy); // clip.Offset(dx, dy);
return GetUpdateRegion().Contains(getRotation().tr(v.boundingBox().toRect()).toRect()) != wxOutRegion; return GetUpdateRegion().Contains(getRotation().trRectToBB(v.boundingBox().toRect()).toRect()) != wxOutRegion;
} }
// helper class for overdrawDC() // helper class for overdrawDC()
...@@ -133,7 +133,7 @@ Rotation CardViewer::getRotation() const { ...@@ -133,7 +133,7 @@ Rotation CardViewer::getRotation() const {
if (!stylesheet) stylesheet = set->stylesheet; if (!stylesheet) stylesheet = set->stylesheet;
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet); StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet);
int dx = GetScrollPos(wxHORIZONTAL), dy = GetScrollPos(wxVERTICAL); int dx = GetScrollPos(wxHORIZONTAL), dy = GetScrollPos(wxVERTICAL);
return Rotation(ss.card_angle(), stylesheet->getCardRect().move(-dx,-dy,0,0), ss.card_zoom(), 1.0, true); return Rotation(ss.card_angle(), stylesheet->getCardRect().move(-dx,-dy,0,0), ss.card_zoom(), 1.0, ROTATION_ATTACH_TOP_LEFT);
} }
// ----------------------------------------------------------------------------- : Event table // ----------------------------------------------------------------------------- : Event table
......
...@@ -39,14 +39,14 @@ void NativeLookEditor::drawViewer(RotatedDC& dc, ValueViewer& v) { ...@@ -39,14 +39,14 @@ void NativeLookEditor::drawViewer(RotatedDC& dc, ValueViewer& v) {
Style& s = *v.getStyle(); Style& s = *v.getStyle();
dc.SetPen(*wxTRANSPARENT_PEN); dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
dc.DrawRectangle(s.getRect().grow(1)); dc.DrawRectangle(s.getInternalRect().grow(1));
// draw label // draw label
dc.SetFont(*wxNORMAL_FONT); dc.SetFont(*wxNORMAL_FONT);
// TODO : tr using stylesheet or using game? // TODO : tr using stylesheet or using game?
dc.DrawText(tr(*set->game, s.fieldP->name, capitalize_sentence(s.fieldP->name)), dc.DrawText(tr(*set->game, s.fieldP->name, capitalize_sentence(s.fieldP->name)),
RealPoint(margin_left, s.top + 1)); RealPoint(margin_left - s.left, 1));
// draw 3D border // draw 3D border
draw_control_border(this, dc.getDC(), dc.tr(RealRect(s.left - 1, s.top - 1, s.width + 2, s.height + 2))); draw_control_border(this, dc.getDC(), dc.trRectStraight(s.getInternalRect().grow(1)));
} }
// draw viewer // draw viewer
v.draw(dc); v.draw(dc);
...@@ -152,10 +152,10 @@ void NativeLookEditor::onScroll(wxScrollWinEvent& ev) { ...@@ -152,10 +152,10 @@ void NativeLookEditor::onScroll(wxScrollWinEvent& ev) {
} }
void NativeLookEditor::onMouseWheel(wxMouseEvent& ev) { void NativeLookEditor::onMouseWheel(wxMouseEvent& ev) {
// send scroll event to field under cursor // send scroll event to field under cursor
RealPoint pos = mousePoint(ev);
FOR_EACH_EDITOR_REVERSE { // find high z index fields first FOR_EACH_EDITOR_REVERSE { // find high z index fields first
RealPoint pos = mousePoint(ev, *v);
if (v->containsPoint(pos) && v->getField()->editable) { if (v->containsPoint(pos) && v->getField()->editable) {
bool scrolled = e->onMouseWheel(mousePoint(ev), ev); bool scrolled = e->onMouseWheel(pos, ev);
if (scrolled) return; if (scrolled) return;
break; break;
} }
......
...@@ -112,7 +112,9 @@ void DropDownList::show(bool in_place, wxPoint pos, RealRect* rect) { ...@@ -112,7 +112,9 @@ void DropDownList::show(bool in_place, wxPoint pos, RealRect* rect) {
int parent_height = 0; int parent_height = 0;
if (!in_place && viewer) { if (!in_place && viewer) {
// Position the drop down list below the editor control (based on the style) // Position the drop down list below the editor control (based on the style)
RealRect r = viewer->viewer.getRotation().trNoNeg(rect ? *rect : viewer->getStyle()->getRect()); Rotation rot = viewer->viewer.getRotation();
Rotater rr(rot, viewer->getRotation());
RealRect r = rot.trRectToBB(rect ? *rect : rot.getInternalRect());
if (viewer->viewer.nativeLook()) { if (viewer->viewer.nativeLook()) {
pos = RealPoint(r.x - 3, r.y - 3); pos = RealPoint(r.x - 3, r.y - 3);
size.width = max(size.width, r.width + 6); size.width = max(size.width, r.width + 6);
...@@ -233,7 +235,8 @@ void DropDownList::redrawArrowOnParent() { ...@@ -233,7 +235,8 @@ void DropDownList::redrawArrowOnParent() {
CardEditor& editor = static_cast<CardEditor&>(viewer->viewer); CardEditor& editor = static_cast<CardEditor&>(viewer->viewer);
shared_ptr<RotatedDC> dcP = editor.overdrawDC(); shared_ptr<RotatedDC> dcP = editor.overdrawDC();
RotatedDC& dc = *dcP; RotatedDC& dc = *dcP;
draw_drop_down_arrow(&editor, dc.getDC(), dc.tr(viewer->getStyle()->getRect().grow(1)), IsShown()); Rotater r(dc, viewer->getRotation());
draw_drop_down_arrow(&editor, dc.getDC(), dc.trRectToBB(dc.getInternalRect().grow(1)), IsShown());
} }
} }
} }
......
...@@ -465,7 +465,7 @@ void ImageSliceSelector::draw(DC& dc) { ...@@ -465,7 +465,7 @@ void ImageSliceSelector::draw(DC& dc) {
dc.DestroyClippingRegion(); dc.DestroyClippingRegion();
} }
// bitmap : unselected // bitmap : unselected
dc.DrawBitmap(bitmap_no_sel, border, border); dc.DrawBitmap(bitmap_no_sel, border, border, true);
// draw selected part ungreyed over it // draw selected part ungreyed over it
{ {
wxMemoryDC mdc; wxMemoryDC mdc;
......
...@@ -57,7 +57,8 @@ void SymbolSelectEditor::draw(DC& dc) { ...@@ -57,7 +57,8 @@ void SymbolSelectEditor::draw(DC& dc) {
// draw selection rectangle // draw selection rectangle
dc.SetBrush(*wxTRANSPARENT_BRUSH); dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.SetPen(wxPen(*wxCYAN,1,wxDOT)); dc.SetPen(wxPen(*wxCYAN,1,wxDOT));
RealRect rect = control.rotation.tr(RealRect(selection_rect_a, RealSize(selection_rect_b - selection_rect_a))); //% TODO: use RotatedDC?
RealRect rect = control.rotation.trRectToBB(RealRect(selection_rect_a, RealSize(selection_rect_b - selection_rect_a)));
dc.DrawRectangle(rect); dc.DrawRectangle(rect);
} else { } else {
// draw handles // draw handles
......
...@@ -299,7 +299,7 @@ void ChoiceValueEditor::onLoseFocus() { ...@@ -299,7 +299,7 @@ void ChoiceValueEditor::onLoseFocus() {
void ChoiceValueEditor::draw(RotatedDC& dc) { void ChoiceValueEditor::draw(RotatedDC& dc) {
ChoiceValueViewer::draw(dc); ChoiceValueViewer::draw(dc);
if (nativeLook()) { if (nativeLook()) {
draw_drop_down_arrow(&editor(), dc.getDC(), dc.tr(style().getRect().grow(1)), drop_down->IsShown()); draw_drop_down_arrow(&editor(), dc.getDC(), dc.trRectStraight(style().getInternalRect().grow(1)), drop_down->IsShown());
} }
} }
void ChoiceValueEditor::determineSize(bool) { void ChoiceValueEditor::determineSize(bool) {
......
...@@ -141,7 +141,7 @@ void ColorValueEditor::onLoseFocus() { ...@@ -141,7 +141,7 @@ void ColorValueEditor::onLoseFocus() {
void ColorValueEditor::draw(RotatedDC& dc) { void ColorValueEditor::draw(RotatedDC& dc) {
ColorValueViewer::draw(dc); ColorValueViewer::draw(dc);
if (nativeLook()) { if (nativeLook()) {
draw_drop_down_arrow(&editor(), dc.getDC(), dc.tr(style().getRect().grow(1)), drop_down->IsShown()); draw_drop_down_arrow(&editor(), dc.getDC(), dc.trRectStraight(dc.getInternalRect().grow(1)), drop_down->IsShown());
} }
} }
void ColorValueEditor::determineSize(bool) { void ColorValueEditor::determineSize(bool) {
......
...@@ -25,7 +25,7 @@ void SymbolValueEditor::draw(RotatedDC& dc) { ...@@ -25,7 +25,7 @@ void SymbolValueEditor::draw(RotatedDC& dc) {
dc.SetFont(wxFont(10,wxSWISS,wxNORMAL,wxNORMAL)); dc.SetFont(wxFont(10,wxSWISS,wxNORMAL,wxNORMAL));
dc.SetTextForeground(*wxBLACK); dc.SetTextForeground(*wxBLACK);
RealSize text_size = dc.GetTextExtent(_("double click to edit symbol")); RealSize text_size = dc.GetTextExtent(_("double click to edit symbol"));
dc.DrawText(_("double click to edit symbol"), align_in_rect(ALIGN_MIDDLE_CENTER, text_size, style().getRect())); dc.DrawText(_("double click to edit symbol"), align_in_rect(ALIGN_MIDDLE_CENTER, text_size, style().getInternalRect()));
} }
if (nativeLook()) { if (nativeLook()) {
// draw editor buttons // draw editor buttons
...@@ -38,8 +38,8 @@ void SymbolValueEditor::drawButton(RotatedDC& dc, int button, const String& text ...@@ -38,8 +38,8 @@ void SymbolValueEditor::drawButton(RotatedDC& dc, int button, const String& text
bool down = button == button_down; bool down = button == button_down;
double height = style().height; double height = style().height;
double width = style().height + 2; double width = style().height + 2;
double x = style().right - width - (width + 1) * button; double x = style().width - width - (width + 1) * button;
double y = style().top; double y = 0;
// draw button // draw button
dc.SetPen(*wxTRANSPARENT_PEN); dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
...@@ -60,8 +60,8 @@ void SymbolValueEditor::drawButton(RotatedDC& dc, int button, const String& text ...@@ -60,8 +60,8 @@ void SymbolValueEditor::drawButton(RotatedDC& dc, int button, const String& text
} }
int SymbolValueEditor::findButton(const RealPoint& pos) { int SymbolValueEditor::findButton(const RealPoint& pos) {
if (pos.y < style().top || pos.y >= style().bottom) return -1; if (pos.y < 0 || pos.y >= style().height) return -1;
int button = (int)floor( (style().right - pos.x) / (style().height + 3) ); int button = (int)floor( (style().width - pos.x) / (style().height + 3) );
if (button >= 0 && button <= 1) return button; if (button >= 0 && button <= 1) return button;
return -1; return -1;
} }
......
...@@ -333,15 +333,14 @@ TextValueEditor::~TextValueEditor() { ...@@ -333,15 +333,14 @@ TextValueEditor::~TextValueEditor() {
bool TextValueEditor::onLeftDown(const RealPoint& pos, wxMouseEvent& ev) { bool TextValueEditor::onLeftDown(const RealPoint& pos, wxMouseEvent& ev) {
select_words = false; select_words = false;
RealPoint pos2 = style().getRotation().trInv(pos);
// on word list dropdown button? // on word list dropdown button?
WordListPosP wl_pos = findWordList(pos2); WordListPosP wl_pos = findWordList(pos);
if (wl_pos) { if (wl_pos) {
wordListDropDown(wl_pos); wordListDropDown(wl_pos);
} else { } else {
// no, select text // no, select text
selecting = true; selecting = true;
moveSelection(TYPE_INDEX, v.indexAt(pos2), !ev.ShiftDown(), MOVE_MID); moveSelection(TYPE_INDEX, v.indexAt(pos), !ev.ShiftDown(), MOVE_MID);
} }
return true; return true;
} }
...@@ -354,7 +353,7 @@ bool TextValueEditor::onLeftUp(const RealPoint& pos, wxMouseEvent&) { ...@@ -354,7 +353,7 @@ bool TextValueEditor::onLeftUp(const RealPoint& pos, wxMouseEvent&) {
bool TextValueEditor::onMotion(const RealPoint& pos, wxMouseEvent& ev) { bool TextValueEditor::onMotion(const RealPoint& pos, wxMouseEvent& ev) {
if (dropDownShown()) return false; if (dropDownShown()) return false;
if (ev.LeftIsDown() && selecting) { if (ev.LeftIsDown() && selecting) {
size_t index = v.indexAt(style().getRotation().trInv(pos)); size_t index = v.indexAt(pos);
if (select_words) { if (select_words) {
// on the left, swap start and end // on the left, swap start and end
bool left = selection_end_i < selection_start_i; bool left = selection_end_i < selection_start_i;
...@@ -385,7 +384,7 @@ bool TextValueEditor::onLeftDClick(const RealPoint& pos, wxMouseEvent& ev) { ...@@ -385,7 +384,7 @@ bool TextValueEditor::onLeftDClick(const RealPoint& pos, wxMouseEvent& ev) {
if (dropDownShown()) return false; if (dropDownShown()) return false;
select_words = true; select_words = true;
selecting = true; selecting = true;
size_t index = v.indexAt(style().getRotation().trInv(pos)); size_t index = v.indexAt(pos);
moveSelection(TYPE_INDEX, prevWordBoundary(index), true, MOVE_MID); moveSelection(TYPE_INDEX, prevWordBoundary(index), true, MOVE_MID);
moveSelection(TYPE_INDEX, nextWordBoundary(index), false, MOVE_MID); moveSelection(TYPE_INDEX, nextWordBoundary(index), false, MOVE_MID);
return true; return true;
...@@ -393,7 +392,7 @@ bool TextValueEditor::onLeftDClick(const RealPoint& pos, wxMouseEvent& ev) { ...@@ -393,7 +392,7 @@ bool TextValueEditor::onLeftDClick(const RealPoint& pos, wxMouseEvent& ev) {
bool TextValueEditor::onRightDown(const RealPoint& pos, wxMouseEvent& ev) { bool TextValueEditor::onRightDown(const RealPoint& pos, wxMouseEvent& ev) {
if (dropDownShown()) return false; if (dropDownShown()) return false;
size_t index = v.indexAt(style().getRotation().trInv(pos)); size_t index = v.indexAt(pos);
if (index < min(selection_start_i, selection_end_i) || if (index < min(selection_start_i, selection_end_i) ||
index > max(selection_start_i, selection_end_i)) { index > max(selection_start_i, selection_end_i)) {
// only move cursor when outside selection // only move cursor when outside selection
...@@ -587,9 +586,10 @@ void TextValueEditor::redrawSelection(size_t old_selection_start_i, size_t old_s ...@@ -587,9 +586,10 @@ void TextValueEditor::redrawSelection(size_t old_selection_start_i, size_t old_s
// Move selection // Move selection
shared_ptr<RotatedDC> dcP = editor().overdrawDC(); shared_ptr<RotatedDC> dcP = editor().overdrawDC();
RotatedDC& dc = *dcP; RotatedDC& dc = *dcP;
Rotater r(dc, getRotation());
if (nativeLook()) { if (nativeLook()) {
// clip the dc to the region of this control // clip the dc to the region of this control
dc.SetClippingRegion(style().getRect()); dc.SetClippingRegion(style().getInternalRect());
} }
// clear old selection by drawing it again // clear old selection by drawing it again
if (!old_drop_down_shown) { if (!old_drop_down_shown) {
...@@ -622,8 +622,7 @@ void TextValueEditor::redrawSelection(size_t old_selection_start_i, size_t old_s ...@@ -622,8 +622,7 @@ void TextValueEditor::redrawSelection(size_t old_selection_start_i, size_t old_s
wxCursor rotated_ibeam; wxCursor rotated_ibeam;
wxCursor TextValueEditor::cursor(const RealPoint& pos) const { wxCursor TextValueEditor::cursor(const RealPoint& pos) const {
RealPoint pos2 = style().getRotation().trInv(pos); WordListPosP p = findWordList(pos);
WordListPosP p = findWordList(pos2);
if (p) { if (p) {
if (hovered_words != p.get()) { if (hovered_words != p.get()) {
hovered_words = p.get(); hovered_words = p.get();
...@@ -631,12 +630,13 @@ wxCursor TextValueEditor::cursor(const RealPoint& pos) const { ...@@ -631,12 +630,13 @@ wxCursor TextValueEditor::cursor(const RealPoint& pos) const {
} }
return wxCursor(); return wxCursor();
} else { } else {
p = findWordListBody(pos2); p = findWordListBody(pos);
if (hovered_words != p.get()) { if (hovered_words != p.get()) {
hovered_words = p.get(); hovered_words = p.get();
const_cast<TextValueEditor*>(this)->redrawWordListIndicators(); const_cast<TextValueEditor*>(this)->redrawWordListIndicators();
} }
if (viewer.getRotation().sideways() ^ style().getRotation().sideways()) { // 90 or 270 degrees int angle = viewer.getRotation().getAngle() + style().angle;
if (sideways(angle)) { // 90 or 270 degrees
if (!rotated_ibeam.Ok()) { if (!rotated_ibeam.Ok()) {
rotated_ibeam = wxCursor(load_resource_cursor(_("rot_text"))); rotated_ibeam = wxCursor(load_resource_cursor(_("rot_text")));
} }
...@@ -648,17 +648,16 @@ wxCursor TextValueEditor::cursor(const RealPoint& pos) const { ...@@ -648,17 +648,16 @@ wxCursor TextValueEditor::cursor(const RealPoint& pos) const {
} }
bool TextValueEditor::containsPoint(const RealPoint& pos) const { bool TextValueEditor::containsPoint(const RealPoint& pos) const {
if (TextValueViewer::containsPoint(pos)) return true; RealPoint pos2(pos.x * style().getStretch(), pos.y);
if (TextValueViewer::containsPoint(pos2)) return true;
if (word_lists.empty()) return false; if (word_lists.empty()) return false;
RealPoint pos2 = style().getRotation().trInv(pos); return findWordList(pos);
return findWordList(pos2);
} }
RealRect TextValueEditor::boundingBox() const { RealRect TextValueEditor::boundingBox() const {
if (word_lists.empty()) return ValueViewer::boundingBox(); if (word_lists.empty()) return ValueViewer::boundingBox();
RealRect r = style().getRect().grow(1); RealRect r = style().getInternalRect().grow(1);
Rotation rot = style().getRotation();
FOR_EACH_CONST(wl, word_lists) { FOR_EACH_CONST(wl, word_lists) {
r.width = max(r.width, rot.tr(wl->rect).right() + 9); r.width = max(r.width, wl->rect.right() + 9);
} }
return r; return r;
} }
...@@ -794,7 +793,7 @@ void TextValueEditor::showCaret() { ...@@ -794,7 +793,7 @@ void TextValueEditor::showCaret() {
} }
// Rotation // Rotation
Rotation rot(viewer.getRotation()); Rotation rot(viewer.getRotation());
Rotater rot2(rot, style().getRotation()); Rotater rot2(rot, getRotation());
// The caret // The caret
wxCaret* caret = editor().GetCaret(); wxCaret* caret = editor().GetCaret();
// cursor rectangle // cursor rectangle
...@@ -841,7 +840,8 @@ void TextValueEditor::showCaret() { ...@@ -841,7 +840,8 @@ void TextValueEditor::showCaret() {
} }
} }
// rotate // rotate
cursor = rot.tr(cursor); // TODO: handle rotated cursor
cursor = rot.trRectToBB(cursor);
// set size // set size
wxSize size = cursor.size(); wxSize size = cursor.size();
if (size.GetWidth() == 0) size.SetWidth (1); if (size.GetWidth() == 0) size.SetWidth (1);
...@@ -1154,9 +1154,10 @@ void TextValueEditor::determineSize(bool force_fit) { ...@@ -1154,9 +1154,10 @@ void TextValueEditor::determineSize(bool force_fit) {
if (scrollbar) { if (scrollbar) {
// muliline, determine scrollbar size // muliline, determine scrollbar size
Rotation rot = viewer.getRotation(); Rotation rot = viewer.getRotation();
Rotater r(rot, getRotation());
if (!force_fit) style().height = 100; if (!force_fit) style().height = 100;
int sbw = wxSystemSettings::GetMetric(wxSYS_VSCROLL_X); int sbw = wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
RealPoint pos = rot.tr(style().getPos()); RealPoint pos = rot.tr(RealPoint(0,0));
scrollbar->SetSize( scrollbar->SetSize(
(int)(pos.x + rot.trX(style().width) + 1 - sbw), (int)(pos.x + rot.trX(style().width) + 1 - sbw),
(int)pos.y - 1, (int)pos.y - 1,
...@@ -1275,7 +1276,6 @@ void TextValueEditor::findWordLists() { ...@@ -1275,7 +1276,6 @@ void TextValueEditor::findWordLists() {
void TextValueEditor::clearWordListIndicators(RotatedDC& dc) { void TextValueEditor::clearWordListIndicators(RotatedDC& dc) {
if (word_lists.empty()) return; if (word_lists.empty()) return;
Rotater rot(dc, style().getRotation());
bool current = isCurrent(); bool current = isCurrent();
FOR_EACH(wl, word_lists) { FOR_EACH(wl, word_lists) {
if (current && drop_down && drop_down->IsShown() && drop_down->getPos() == wl) { if (current && drop_down && drop_down->IsShown() && drop_down->getPos() == wl) {
...@@ -1287,7 +1287,7 @@ void TextValueEditor::clearWordListIndicators(RotatedDC& dc) { ...@@ -1287,7 +1287,7 @@ void TextValueEditor::clearWordListIndicators(RotatedDC& dc) {
} }
// restore background // restore background
if (wl->behind.Ok()) { if (wl->behind.Ok()) {
dc.DrawPreRotatedBitmap(wl->behind, wl->rect.topRight() + RealSize(0,-1)); dc.DrawPreRotatedBitmap(wl->behind, RealRect(wl->rect.right(), wl->rect.y - 1, 10, wl->rect.height+3));
} }
} }
} }
...@@ -1298,7 +1298,6 @@ void TextValueEditor::redrawWordListIndicators(bool toggling_dropdown) { ...@@ -1298,7 +1298,6 @@ void TextValueEditor::redrawWordListIndicators(bool toggling_dropdown) {
void TextValueEditor::drawWordListIndicators(RotatedDC& dc, bool redrawing) { void TextValueEditor::drawWordListIndicators(RotatedDC& dc, bool redrawing) {
if (word_lists.empty()) return; if (word_lists.empty()) return;
Rotater rot(dc, style().getRotation());
bool current = isCurrent(); bool current = isCurrent();
// Draw lines around fields // Draw lines around fields
FOR_EACH(wl, word_lists) { FOR_EACH(wl, word_lists) {
...@@ -1410,7 +1409,7 @@ bool TextValueEditor::wordListDropDown(const WordListPosP& wl) { ...@@ -1410,7 +1409,7 @@ bool TextValueEditor::wordListDropDown(const WordListPosP& wl) {
} else { } else {
drop_down.reset(new DropDownWordList(&editor(), false, *this, wl, wl->word_list)); drop_down.reset(new DropDownWordList(&editor(), false, *this, wl, wl->word_list));
} }
RealRect rect = style().getRotation().tr(wl->rect).move(0, -1, 0, 2); RealRect rect = wl->rect.grow(1);
drop_down->show(false, wxPoint(0,0), &rect); drop_down->show(false, wxPoint(0,0), &rect);
return true; return true;
} }
...@@ -995,6 +995,12 @@ ...@@ -995,6 +995,12 @@
<File <File
RelativePath=".\gui\new_window.hpp"> RelativePath=".\gui\new_window.hpp">
</File> </File>
<File
RelativePath=".\gui\packages_window.cpp">
</File>
<File
RelativePath=".\gui\packages_window.hpp">
</File>
<File <File
RelativePath=".\gui\preferences_window.cpp"> RelativePath=".\gui\preferences_window.cpp">
</File> </File>
...@@ -2023,6 +2029,12 @@ ...@@ -2023,6 +2029,12 @@
<File <File
RelativePath=".\util\io\package_manager.hpp"> RelativePath=".\util\io\package_manager.hpp">
</File> </File>
<File
RelativePath=".\util\io\package_repository.cpp">
</File>
<File
RelativePath=".\util\io\package_repository.hpp">
</File>
<File <File
RelativePath=".\util\io\reader.cpp"> RelativePath=".\util\io\reader.cpp">
<FileConfiguration <FileConfiguration
......
...@@ -44,6 +44,7 @@ void DataViewer::draw(RotatedDC& dc, const Color& background) { ...@@ -44,6 +44,7 @@ void DataViewer::draw(RotatedDC& dc, const Color& background) {
bool changed_content_properties = false; bool changed_content_properties = false;
FOR_EACH(v, viewers) { // draw low z index fields first FOR_EACH(v, viewers) { // draw low z index fields first
if (v->getStyle()->visible) { if (v->getStyle()->visible) {
Rotater r(dc, v->getRotation());
try { try {
if (v->prepare(dc)) { if (v->prepare(dc)) {
changed_content_properties = true; changed_content_properties = true;
...@@ -59,6 +60,7 @@ void DataViewer::draw(RotatedDC& dc, const Color& background) { ...@@ -59,6 +60,7 @@ void DataViewer::draw(RotatedDC& dc, const Color& background) {
// draw viewers // draw viewers
FOR_EACH(v, viewers) { // draw low z index fields first FOR_EACH(v, viewers) { // draw low z index fields first
if (v->getStyle()->visible) {// visible if (v->getStyle()->visible) {// visible
Rotater r(dc, v->getRotation());
try { try {
drawViewer(dc, *v); drawViewer(dc, *v);
} catch (const Error& e) { } catch (const Error& e) {
...@@ -104,7 +106,7 @@ Context& DataViewer::getContext() const { return set->getContext(card); } ...@@ -104,7 +106,7 @@ Context& DataViewer::getContext() const { return set->getContext(card); }
Rotation DataViewer::getRotation() const { Rotation DataViewer::getRotation() const {
if (!stylesheet) stylesheet = set->stylesheet; if (!stylesheet) stylesheet = set->stylesheet;
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet); StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet);
return Rotation(ss.card_angle(), stylesheet->getCardRect(), ss.card_zoom(), 1.0, true); return Rotation(ss.card_angle(), stylesheet->getCardRect(), ss.card_zoom(), 1.0, ROTATION_ATTACH_TOP_LEFT);
} }
// ----------------------------------------------------------------------------- : Setting data // ----------------------------------------------------------------------------- : Setting data
...@@ -154,8 +156,8 @@ void DataViewer::addStyles(IndexMap<FieldP,StyleP>& styles) { ...@@ -154,8 +156,8 @@ void DataViewer::addStyles(IndexMap<FieldP,StyleP>& styles) {
FOR_EACH(s, styles) { FOR_EACH(s, styles) {
if ((s->visible || s->visible.isScripted()) && if ((s->visible || s->visible.isScripted()) &&
(nativeLook() || ( (nativeLook() || (
(s->width || s->width .isScripted() || s->right || s->right .isScripted()) && (s->width != -1 || s->width .isScripted() || s->right != -1 || s->right .isScripted()) &&
(s->height || s->height .isScripted() || s->bottom || s->bottom.isScripted())))) { (s->height != -1 || s->height .isScripted() || s->bottom != -1 || s->bottom.isScripted())))) {
// no need to make a viewer for things that are always invisible // no need to make a viewer for things that are always invisible
ValueViewerP viewer = makeViewer(s); ValueViewerP viewer = makeViewer(s);
if (viewer) viewers.push_back(viewer); if (viewer) viewers.push_back(viewer);
......
...@@ -365,7 +365,7 @@ void SymbolViewer::highlightPart(DC& dc, const SymbolGroup& group, HighlightStyl ...@@ -365,7 +365,7 @@ void SymbolViewer::highlightPart(DC& dc, const SymbolGroup& group, HighlightStyl
if (style == HIGHLIGHT_BORDER) { if (style == HIGHLIGHT_BORDER) {
dc.SetBrush(*wxTRANSPARENT_BRUSH); dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.SetPen (wxPen(Color(255,0,0), 2)); dc.SetPen (wxPen(Color(255,0,0), 2));
dc.DrawRectangle(rotation.tr(RealRect(group.min_pos, RealSize(group.max_pos - group.min_pos)))); dc.DrawRectangle(rotation.trRectToBB(RealRect(group.min_pos, RealSize(group.max_pos - group.min_pos))));
} }
FOR_EACH_CONST(part, group.parts) { FOR_EACH_CONST(part, group.parts) {
highlightPart(dc, *part, (HighlightStyle)(style | HIGHLIGHT_LESS)); highlightPart(dc, *part, (HighlightStyle)(style | HIGHLIGHT_LESS));
......
...@@ -39,7 +39,7 @@ struct TextViewer::Line { ...@@ -39,7 +39,7 @@ struct TextViewer::Line {
/// Is this line visible using the given rectangle? /// Is this line visible using the given rectangle?
bool visible(const Rotation& rot) const { bool visible(const Rotation& rot) const {
return top + line_height > 0 && top < rot.getInternalSize().height; return top + line_height > 0 && top < rot.getHeight();
} }
/// Get a rectangle of the selection on this line /// Get a rectangle of the selection on this line
...@@ -68,7 +68,6 @@ TextViewer::~TextViewer() {} ...@@ -68,7 +68,6 @@ TextViewer::~TextViewer() {}
void TextViewer::draw(RotatedDC& dc, const TextStyle& style, DrawWhat what) { void TextViewer::draw(RotatedDC& dc, const TextStyle& style, DrawWhat what) {
assert(!lines.empty()); assert(!lines.empty());
Rotater r(dc, style.getRotation());
// separator lines? // separator lines?
// do this first, so pen is still set from drawing the field border // do this first, so pen is still set from drawing the field border
if (what & DRAW_BORDERS) { if (what & DRAW_BORDERS) {
...@@ -99,7 +98,6 @@ RealRect intersect(const RealRect& a, const RealRect& b) { ...@@ -99,7 +98,6 @@ RealRect intersect(const RealRect& a, const RealRect& b) {
} }
void TextViewer::drawSelection(RotatedDC& dc, const TextStyle& style, size_t sel_start, size_t sel_end) { void TextViewer::drawSelection(RotatedDC& dc, const TextStyle& style, size_t sel_start, size_t sel_end) {
Rotater r(dc, style.getRotation());
if (sel_start == sel_end) return; if (sel_start == sel_end) return;
if (sel_end < sel_start) swap(sel_start, sel_end); if (sel_end < sel_start) swap(sel_start, sel_end);
dc.SetBrush(*wxBLACK_BRUSH); dc.SetBrush(*wxBLACK_BRUSH);
...@@ -148,9 +146,8 @@ void TextViewer::drawSeparators(RotatedDC& dc) { ...@@ -148,9 +146,8 @@ void TextViewer::drawSeparators(RotatedDC& dc) {
} }
bool TextViewer::prepare(RotatedDC& dc, const String& text, TextStyle& style, Context& ctx) { bool TextViewer::prepare(RotatedDC& dc, const String& text, TextStyle& style, Context& ctx) {
if (lines.empty()) { if (!prepared()) {
// not prepared yet // not prepared yet
Rotater r(dc, style.getRotationNoStretch());
prepareElements(text, style, ctx); prepareElements(text, style, ctx);
prepareLines(dc, text, style, ctx); prepareLines(dc, text, style, ctx);
return true; return true;
......
...@@ -27,21 +27,12 @@ bool ChoiceValueViewer::prepare(RotatedDC& dc) { ...@@ -27,21 +27,12 @@ bool ChoiceValueViewer::prepare(RotatedDC& dc) {
ImageCombine combine = style().combine; ImageCombine combine = style().combine;
style().loadMask(*viewer.stylesheet); style().loadMask(*viewer.stylesheet);
Bitmap bitmap; Image image; Bitmap bitmap; Image image;
img.generateCached(img_options, &style().mask, &combine, &bitmap, &image); RealSize size;
int w, h; img.generateCached(img_options, &style().mask, &combine, &bitmap, &image, &size);
if (bitmap.Ok()) {
w = bitmap.GetWidth();
h = bitmap.GetHeight();
} else {
assert(image.Ok());
w = image.GetWidth();
h = image.GetHeight();
}
if (sideways(img_options.angle)) swap(w,h);
// store content properties // store content properties
if (style().content_width != w || style().content_height != h) { if (style().content_width != size.width || style().content_height != size.height) {
style().content_width = w; style().content_width = size.width;
style().content_height = h; style().content_height = size.height;
return true; return true;
} }
} }
...@@ -64,21 +55,18 @@ void ChoiceValueViewer::draw(RotatedDC& dc) { ...@@ -64,21 +55,18 @@ void ChoiceValueViewer::draw(RotatedDC& dc) {
ImageCombine combine = style().combine; ImageCombine combine = style().combine;
style().loadMask(*viewer.stylesheet); style().loadMask(*viewer.stylesheet);
Bitmap bitmap; Image image; Bitmap bitmap; Image image;
img.generateCached(img_options, &style().mask, &combine, &bitmap, &image); RealSize size;
img.generateCached(img_options, &style().mask, &combine, &bitmap, &image, &size);
size = dc.trInvS(size);
RealRect rect(align_in_rect(style().alignment, size, dc.getInternalRect()), size);
if (bitmap.Ok()) { if (bitmap.Ok()) {
// just draw it // just draw it
dc.DrawPreRotatedBitmap(bitmap, dc.DrawPreRotatedBitmap(bitmap,rect);
align_in_rect(style().alignment, dc.trInvNoNeg(RealSize(bitmap)), style().getRect())
);
margin = dc.trInv(RealSize(bitmap)).width + 1;
} else { } else {
// use combine mode // use combine mode
dc.DrawPreRotatedImage(image, dc.DrawPreRotatedImage(image,rect,combine);
align_in_rect(style().alignment, dc.trInvNoNeg(RealSize(image)), style().getRect()),
combine
);
margin = dc.trInv(RealSize(image)).width + 1;
} }
margin = size.width + 1;
} else if (nativeLook()) { } else if (nativeLook()) {
// always have the margin // always have the margin
margin = 17; margin = 17;
...@@ -88,7 +76,7 @@ void ChoiceValueViewer::draw(RotatedDC& dc) { ...@@ -88,7 +76,7 @@ void ChoiceValueViewer::draw(RotatedDC& dc) {
// draw text // draw text
dc.SetFont(style().font, 1.0); dc.SetFont(style().font, 1.0);
String text = tr(*viewer.stylesheet, value().value(), capitalize(value().value())); String text = tr(*viewer.stylesheet, value().value(), capitalize(value().value()));
RealPoint pos = align_in_rect(ALIGN_MIDDLE_LEFT, RealSize(0, dc.GetCharHeight()), style().getRect()) + RealSize(margin, 0); RealPoint pos = align_in_rect(ALIGN_MIDDLE_LEFT, RealSize(0, dc.GetCharHeight()), dc.getInternalRect()) + RealSize(margin, 0);
if (style().font.hasShadow()) { if (style().font.hasShadow()) {
dc.SetTextForeground(style().font.shadow_color); dc.SetTextForeground(style().font.shadow_color);
dc.DrawText(text, pos + style().font.shadow_displacement); dc.DrawText(text, pos + style().font.shadow_displacement);
...@@ -106,7 +94,7 @@ void ChoiceValueViewer::onStyleChange(int changes) { ...@@ -106,7 +94,7 @@ void ChoiceValueViewer::onStyleChange(int changes) {
void ChoiceValueViewer::getOptions(Rotation& rot, GeneratedImage::Options& opts) { void ChoiceValueViewer::getOptions(Rotation& rot, GeneratedImage::Options& opts) {
opts.package = viewer.stylesheet.get(); opts.package = viewer.stylesheet.get();
opts.local_package = &getSet(); opts.local_package = &getSet();
opts.angle = rot.trAngle(style().angle); opts.angle = rot.trAngle(0);
if (nativeLook()) { if (nativeLook()) {
opts.width = opts.height = 16; opts.width = opts.height = 16;
opts.preserve_aspect = ASPECT_BORDER; opts.preserve_aspect = ASPECT_BORDER;
......
...@@ -34,11 +34,11 @@ void ColorValueViewer::draw(RotatedDC& dc) { ...@@ -34,11 +34,11 @@ void ColorValueViewer::draw(RotatedDC& dc) {
} }
// draw name and color // draw name and color
dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
dc.DrawRectangle(RealRect(style().left, style().top, 40, style().height)); dc.DrawRectangle(RealRect(0, 0, 40, dc.getHeight()));
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
dc.SetPen(*wxTRANSPARENT_PEN); dc.SetPen(*wxTRANSPARENT_PEN);
dc.DrawRectangle(style().getRect().move(40, 0, -40, 0)); dc.DrawRectangle(RealRect(40, 0, dc.getWidth()-40, dc.getHeight()));
dc.DrawText(color_name, style().getPos() + RealSize(43, 3)); dc.DrawText(color_name, RealPoint(43, 3));
} else { } else {
// is there a mask? // is there a mask?
loadMask(dc); loadMask(dc);
...@@ -53,8 +53,8 @@ void ColorValueViewer::draw(RotatedDC& dc) { ...@@ -53,8 +53,8 @@ void ColorValueViewer::draw(RotatedDC& dc) {
style().top_width < style().height && style().bottom_width < style().height; style().top_width < style().height && style().bottom_width < style().height;
if (clip) { if (clip) {
// clip away the inside of the rectangle // clip away the inside of the rectangle
wxRegion r = dc.tr(style().getRect()).toRect(); wxRegion r = dc.trRectToRegion(style().getInternalRect());
r.Subtract(dc.tr(RealRect( r.Subtract(dc.trRectToRegion(RealRect(
style().left + style().left_width, style().left + style().left_width,
style().top + style().top_width, style().top + style().top_width,
style().width - style().left_width - style().right_width, style().width - style().left_width - style().right_width,
...@@ -62,7 +62,7 @@ void ColorValueViewer::draw(RotatedDC& dc) { ...@@ -62,7 +62,7 @@ void ColorValueViewer::draw(RotatedDC& dc) {
))); )));
dc.getDC().SetClippingRegion(r); dc.getDC().SetClippingRegion(r);
} }
dc.DrawRoundedRectangle(style().getRect(), style().radius); dc.DrawRoundedRectangle(style().getInternalRect(), style().radius);
if (clip) dc.getDC().DestroyClippingRegion(); if (clip) dc.getDC().DestroyClippingRegion();
} }
} }
...@@ -70,8 +70,8 @@ void ColorValueViewer::draw(RotatedDC& dc) { ...@@ -70,8 +70,8 @@ void ColorValueViewer::draw(RotatedDC& dc) {
bool ColorValueViewer::containsPoint(const RealPoint& p) const { bool ColorValueViewer::containsPoint(const RealPoint& p) const {
// distance to each side // distance to each side
double left = p.x - style().left, right = style().right - p.x - 1; double left = p.x, right = style().width - p.x - 1;
double top = p.y - style().top, bottom = style().bottom - p.y - 1; double top = p.y, bottom = style().height - p.y - 1;
if (left < 0 || right < 0 || top < 0 || bottom < 0 || // outside bounding box if (left < 0 || right < 0 || top < 0 || bottom < 0 || // outside bounding box
(left >= style().left_width && right >= style().right_width && // outside horizontal border (left >= style().left_width && right >= style().right_width && // outside horizontal border
top >= style().top_width && bottom >= style().bottom_width)) { // outside vertical border top >= style().top_width && bottom >= style().bottom_width)) { // outside vertical border
...@@ -93,7 +93,7 @@ void ColorValueViewer::onStyleChange(int changes) { ...@@ -93,7 +93,7 @@ void ColorValueViewer::onStyleChange(int changes) {
void ColorValueViewer::loadMask(const Rotation& rot) const { void ColorValueViewer::loadMask(const Rotation& rot) const {
if (style().mask_filename().empty()) return; // no mask if (style().mask_filename().empty()) return; // no mask
int w = (int) rot.trX(style().width), h = (int) rot.trY(style().height); int w = (int) rot.trX(rot.getWidth()), h = (int) rot.trY(rot.getHeight());
if (alpha_mask && alpha_mask->size == wxSize(w,h)) return; // mask loaded and right size if (alpha_mask && alpha_mask->size == wxSize(w,h)) return; // mask loaded and right size
// (re) load the mask // (re) load the mask
Image image; Image image;
......
...@@ -12,14 +12,15 @@ ...@@ -12,14 +12,15 @@
#include <data/stylesheet.hpp> #include <data/stylesheet.hpp>
#include <gui/util.hpp> #include <gui/util.hpp>
DECLARE_TYPEOF_COLLECTION(wxPoint);
// ----------------------------------------------------------------------------- : ImageValueViewer // ----------------------------------------------------------------------------- : ImageValueViewer
void ImageValueViewer::draw(RotatedDC& dc) { void ImageValueViewer::draw(RotatedDC& dc) {
drawFieldBorder(dc);
// reset? // reset?
int w = (int)dc.trX(style().width), h = (int)dc.trY(style().height); int w = (int)dc.trX(style().width), h = (int)dc.trY(style().height);
int a = dc.trAngle(style().angle); int a = dc.trAngle(0); //% TODO : Add getAngle()?
if (bitmap.Ok() && (a != angle || bitmap.GetWidth() != w || bitmap.GetHeight() != h)) { if (bitmap.Ok() && (a != angle || size.width != w || size.height != h)) {
bitmap = Bitmap(); bitmap = Bitmap();
} }
// try to load image // try to load image
...@@ -65,53 +66,40 @@ void ImageValueViewer::draw(RotatedDC& dc) { ...@@ -65,53 +66,40 @@ void ImageValueViewer::draw(RotatedDC& dc) {
if (image.Ok()) { if (image.Ok()) {
// apply mask and rotate // apply mask and rotate
if (alpha_mask) alpha_mask->setAlpha(image); if (alpha_mask) alpha_mask->setAlpha(image);
size = RealSize(image);
image = rotate_image(image, angle); image = rotate_image(image, angle);
bitmap = Bitmap(image); bitmap = Bitmap(image);
} }
} }
// border
drawFieldBorder(dc);
// draw image, if any // draw image, if any
if (bitmap.Ok()) { if (bitmap.Ok()) {
dc.DrawPreRotatedBitmap(bitmap, style().getPos()); dc.DrawPreRotatedBitmap(bitmap, dc.getInternalRect());
} }
/* }
// if there is no image, generate a placeholder
if (!bitmap.Ok()) { void ImageValueViewer::drawFieldBorder(RotatedDC& dc) {
UInt w = (UInt)dc.trX(style().width), h = (UInt)dc.trY(style().height); if (!alpha_mask) {
loadMask(dc); ValueViewer::drawFieldBorder(dc);
if (style().default_image.isReady()) { } else if (viewer.drawBorders() && field().editable) {
// we have a script to use for the default image dc.SetPen(viewer.borderPen(isCurrent()));
Image img = style().default_image.generate(GeneratedImage::Options(w, h, viewer.stylesheet.get(), &getSet())); dc.SetBrush(*wxTRANSPARENT_BRUSH);
if (viewer.drawEditing()) { vector<wxPoint> points;
bitmap = imagePlaceholder(dc, w, h, img, viewer.drawEditing()); alpha_mask->convexHull(points);
if (alpha_mask) alpha_mask->setAlpha(bitmap); if (points.size() < 3) return;
} else { FOR_EACH(p, points) p = dc.trPixelNoZoom(RealPoint(p.x,p.y));
if (alpha_mask) alpha_mask->setAlpha(img); dc.getDC().DrawPolygon((int)points.size(), &points[0]);
bitmap = Bitmap(img);
}
} else if (style().width > 40) {
// still not okay, use a checkered image, but only if there is enough room for it
bitmap = imagePlaceholder(dc, w, h, wxNullImage, viewer.drawEditing());
if (alpha_mask) alpha_mask->setAlpha(bitmap);
}
}
// draw image, if any
if (bitmap.Ok()) {
dc.DrawBitmap(bitmap, style().getPos());
} }
*/
} }
bool ImageValueViewer::containsPoint(const RealPoint& p) const { bool ImageValueViewer::containsPoint(const RealPoint& p) const {
double x = p.x - style().left; if (!ValueViewer::containsPoint(p)) return false;
double y = p.y - style().top;
if (x < 0 || y < 0 || x >= style().width || y >= style().height) {
return false; // outside rectangle
}
// check against mask // check against mask
if (!style().mask_filename().empty()) { if (!style().mask_filename().empty()) {
loadMask(viewer.getRotation()); loadMask(viewer.getRotation());
Rotation rot = viewer.getRotation(); Rotation rot = viewer.getRotation();
return !alpha_mask || !alpha_mask->isTransparent((int)rot.trX(x), (int)rot.trY(y)); return !alpha_mask || !alpha_mask->isTransparent((int)rot.trX(p.x), (int)rot.trY(p.y));
} else { } else {
return true; return true;
} }
......
...@@ -31,6 +31,7 @@ class ImageValueViewer : public ValueViewer { ...@@ -31,6 +31,7 @@ class ImageValueViewer : public ValueViewer {
private: private:
Bitmap bitmap; ///< Cached bitmap Bitmap bitmap; ///< Cached bitmap
RealSize size; ///< Size of cached bitmap
int angle; ///< Angle of cached bitmap int angle; ///< Angle of cached bitmap
int is_default; ///< Is the default placeholder image used? int is_default; ///< Is the default placeholder image used?
mutable AlphaMaskP alpha_mask; mutable AlphaMaskP alpha_mask;
...@@ -39,6 +40,9 @@ class ImageValueViewer : public ValueViewer { ...@@ -39,6 +40,9 @@ class ImageValueViewer : public ValueViewer {
/// Generate a placeholder image /// Generate a placeholder image
static Bitmap imagePlaceholder(const Rotation& rot, UInt w, UInt h, const Image& background, bool editing); static Bitmap imagePlaceholder(const Rotation& rot, UInt w, UInt h, const Image& background, bool editing);
/// Draws a border around the field
void drawFieldBorder(RotatedDC& dc);
}; };
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
......
...@@ -23,7 +23,7 @@ void InfoValueViewer::draw(RotatedDC& dc) { ...@@ -23,7 +23,7 @@ void InfoValueViewer::draw(RotatedDC& dc) {
dc.SetFont(style().font, 1.0); dc.SetFont(style().font, 1.0);
} }
// draw background // draw background
RealRect rect = style().getRect(); RealRect rect = style().getInternalRect();
dc.DrawRectangle(rect.grow(2)); dc.DrawRectangle(rect.grow(2));
// draw text // draw text
rect = rect.move( rect = rect.move(
......
...@@ -18,7 +18,7 @@ DECLARE_TYPEOF_COLLECTION(String); ...@@ -18,7 +18,7 @@ DECLARE_TYPEOF_COLLECTION(String);
void MultipleChoiceValueViewer::draw(RotatedDC& dc) { void MultipleChoiceValueViewer::draw(RotatedDC& dc) {
drawFieldBorder(dc); drawFieldBorder(dc);
if (style().render_style & RENDER_HIDDEN) return; if (style().render_style & RENDER_HIDDEN) return;
RealPoint pos = align_in_rect(style().alignment, RealSize(0,0), style().getRect()); RealPoint pos = align_in_rect(style().alignment, RealSize(0,0), style().getInternalRect());
// selected choices // selected choices
vector<String> selected; vector<String> selected;
value().get(selected); value().get(selected);
...@@ -55,27 +55,24 @@ void MultipleChoiceValueViewer::draw(RotatedDC& dc) { ...@@ -55,27 +55,24 @@ void MultipleChoiceValueViewer::draw(RotatedDC& dc) {
ImageCombine combine = style().combine; ImageCombine combine = style().combine;
style().loadMask(*viewer.stylesheet); style().loadMask(*viewer.stylesheet);
Bitmap bitmap; Image image; Bitmap bitmap; Image image;
img.generateCached(img_options, &style().mask, &combine, &bitmap, &image); RealSize size;
img.generateCached(img_options, &style().mask, &combine, &bitmap, &image, &size);
size = dc.trInvS(size);
RealRect rect(align_in_rect(style().alignment, size, dc.getInternalRect()), size);
if (bitmap.Ok()) { if (bitmap.Ok()) {
// just draw it // just draw it
dc.DrawPreRotatedBitmap(bitmap, dc.DrawPreRotatedBitmap(bitmap,rect);
align_in_rect(style().alignment, dc.trInvNoNeg(RealSize(bitmap)), style().getRect())
);
margin = dc.trInv(RealSize(bitmap)).width + 1;
} else { } else {
// use combine mode // use combine mode
dc.DrawPreRotatedImage(image, dc.DrawPreRotatedImage(image,rect,combine);
align_in_rect(style().alignment, dc.trInvNoNeg(RealSize(image)), style().getRect()),
combine
);
margin = dc.trInv(RealSize(image)).width + 1;
} }
margin = size.width + 1;
} }
} }
if (style().render_style & RENDER_TEXT) { if (style().render_style & RENDER_TEXT) {
// draw text // draw text
dc.DrawText(tr(*viewer.stylesheet, value().value(), capitalize(value().value())), dc.DrawText(tr(*viewer.stylesheet, value().value(), capitalize(value().value())),
align_in_rect(ALIGN_MIDDLE_LEFT, RealSize(0, dc.GetCharHeight()), style().getRect()) + RealSize(margin, 0) align_in_rect(ALIGN_MIDDLE_LEFT, RealSize(0, dc.GetCharHeight()), dc.getInternalRect()) + RealSize(margin, 0)
); );
} }
// COPY ENDS HERE // COPY ENDS HERE
...@@ -85,7 +82,7 @@ void MultipleChoiceValueViewer::draw(RotatedDC& dc) { ...@@ -85,7 +82,7 @@ void MultipleChoiceValueViewer::draw(RotatedDC& dc) {
void MultipleChoiceValueViewer::drawChoice(RotatedDC& dc, RealPoint& pos, const String& choice, bool active) { void MultipleChoiceValueViewer::drawChoice(RotatedDC& dc, RealPoint& pos, const String& choice, bool active) {
RealSize size; size.height = item_height; RealSize size; size.height = item_height;
if (nativeLook() && (style().render_style & RENDER_CHECKLIST)) { if (nativeLook() && (style().render_style & RENDER_CHECKLIST)) {
wxRect rect = dc.tr(RealRect(pos + RealSize(1,1), RealSize(12,12))); wxRect rect = dc.trRectStraight(RealRect(pos + RealSize(1,1), RealSize(12,12)));
draw_checkbox(nullptr, dc.getDC(), rect, active); // TODO draw_checkbox(nullptr, dc.getDC(), rect, active); // TODO
size = add_horizontal(size, RealSize(14,16)); size = add_horizontal(size, RealSize(14,16));
} }
...@@ -99,8 +96,8 @@ void MultipleChoiceValueViewer::drawChoice(RotatedDC& dc, RealPoint& pos, const ...@@ -99,8 +96,8 @@ void MultipleChoiceValueViewer::drawChoice(RotatedDC& dc, RealPoint& pos, const
Image image = it->second.generate(options); Image image = it->second.generate(options);
ImageCombine combine = it->second.combine(); ImageCombine combine = it->second.combine();
// TODO : alignment? // TODO : alignment?
dc.DrawPreRotatedImage(image, pos + RealSize(size.width, 0), combine == COMBINE_DEFAULT ? style().combine : combine); dc.DrawPreRotatedImage(image, RealRect(pos.x + size.width, pos.y, options.width, options.height), combine == COMBINE_DEFAULT ? style().combine : combine);
size = add_horizontal(size, dc.trInvNoNeg(RealSize(image.GetWidth() + 1, image.GetHeight()))); size.width += options.width;
} }
} }
if (style().render_style & RENDER_TEXT) { if (style().render_style & RENDER_TEXT) {
...@@ -119,7 +116,7 @@ void MultipleChoiceValueViewer::drawChoice(RotatedDC& dc, RealPoint& pos, const ...@@ -119,7 +116,7 @@ void MultipleChoiceValueViewer::drawChoice(RotatedDC& dc, RealPoint& pos, const
void MultipleChoiceValueViewer::getOptions(Rotation& rot, GeneratedImage::Options& opts) { void MultipleChoiceValueViewer::getOptions(Rotation& rot, GeneratedImage::Options& opts) {
opts.package = viewer.stylesheet.get(); opts.package = viewer.stylesheet.get();
opts.local_package = &getSet(); opts.local_package = &getSet();
opts.angle = rot.trAngle(style().angle); opts.angle = rot.trAngle(0); //%%
if (nativeLook()) { if (nativeLook()) {
opts.width = opts.height = 16; opts.width = opts.height = 16;
opts.preserve_aspect = ASPECT_BORDER; opts.preserve_aspect = ASPECT_BORDER;
......
...@@ -20,8 +20,8 @@ DECLARE_TYPEOF_COLLECTION(SymbolVariationP); ...@@ -20,8 +20,8 @@ DECLARE_TYPEOF_COLLECTION(SymbolVariationP);
void SymbolValueViewer::draw(RotatedDC& dc) { void SymbolValueViewer::draw(RotatedDC& dc) {
drawFieldBorder(dc); drawFieldBorder(dc);
// draw checker background // draw checker background
draw_checker(dc, style().getRect()); draw_checker(dc, style().getInternalRect());
double wh = min(style().width, style().height); double wh = min(dc.getWidth(), dc.getHeight());
// try to load symbol // try to load symbol
if (symbols.empty() && !value().filename.empty()) { if (symbols.empty() && !value().filename.empty()) {
try { try {
...@@ -45,7 +45,7 @@ void SymbolValueViewer::draw(RotatedDC& dc) { ...@@ -45,7 +45,7 @@ void SymbolValueViewer::draw(RotatedDC& dc) {
int x = 0; int x = 0;
for (size_t i = 0 ; i < symbols.size() ; ++i) { for (size_t i = 0 ; i < symbols.size() ; ++i) {
// todo : labels? // todo : labels?
dc.DrawBitmap(symbols[i], style().getPos() + RealSize(x, 0)); dc.DrawBitmap(symbols[i], RealPoint(x, 0));
x += symbols[i].GetWidth() + 2; x += symbols[i].GetWidth() + 2;
} }
} }
......
...@@ -28,6 +28,7 @@ void TextValueViewer::draw(RotatedDC& dc) { ...@@ -28,6 +28,7 @@ void TextValueViewer::draw(RotatedDC& dc) {
drawFieldBorder(dc); drawFieldBorder(dc);
if (!v.prepared()) { if (!v.prepared()) {
v.prepare(dc, value().value(), style(), viewer.getContext()); v.prepare(dc, value().value(), style(), viewer.getContext());
dc.setStretch(getStretch());
} }
if (viewer.drawFocus() && isCurrent()) { if (viewer.drawFocus() && isCurrent()) {
v.draw(dc, style(), DRAW_ACTIVE); v.draw(dc, style(), DRAW_ACTIVE);
...@@ -50,3 +51,7 @@ void TextValueViewer::onStyleChange(int changes) { ...@@ -50,3 +51,7 @@ void TextValueViewer::onStyleChange(int changes) {
void TextValueViewer::onAction(const Action&, bool undone) { void TextValueViewer::onAction(const Action&, bool undone) {
v.reset(true); v.reset(true);
} }
double TextValueViewer::getStretch() const {
return v.prepared() ? style().getStretch() : 1.0;
}
...@@ -26,6 +26,7 @@ class TextValueViewer : public ValueViewer { ...@@ -26,6 +26,7 @@ class TextValueViewer : public ValueViewer {
virtual void onValueChange(); virtual void onValueChange();
virtual void onStyleChange(int); virtual void onStyleChange(int);
virtual void onAction(const Action&, bool undone); virtual void onAction(const Action&, bool undone);
virtual double getStretch() const;
protected: protected:
TextViewer v; TextViewer v;
......
...@@ -32,20 +32,24 @@ void ValueViewer::setValue(const ValueP& value) { ...@@ -32,20 +32,24 @@ void ValueViewer::setValue(const ValueP& value) {
} }
bool ValueViewer::containsPoint(const RealPoint& p) const { bool ValueViewer::containsPoint(const RealPoint& p) const {
return p.x >= styleP->left return p.x >= 0
&& p.y >= styleP->top && p.y >= 0
&& p.x < styleP->left + (int)(styleP->width) && p.x < styleP->width
&& p.y < styleP->top + (int)(styleP->height); && p.y < styleP->height;
} }
RealRect ValueViewer::boundingBox() const { RealRect ValueViewer::boundingBox() const {
return styleP->getRect().grow(1); return styleP->getExternalRect().grow(1);
}
Rotation ValueViewer::getRotation() const {
return Rotation(getStyle()->angle, getStyle()->getExternalRect(), 1.0, getStretch());
} }
void ValueViewer::drawFieldBorder(RotatedDC& dc) { void ValueViewer::drawFieldBorder(RotatedDC& dc) {
if (viewer.drawBorders() && getField()->editable) { if (viewer.drawBorders() && getField()->editable) {
dc.SetPen(viewer.borderPen(isCurrent())); dc.SetPen(viewer.borderPen(isCurrent()));
dc.SetBrush(*wxTRANSPARENT_BRUSH); dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(styleP->getRect().grow(dc.trInvS(1))); dc.DrawRectangle(dc.getInternalRect().grow(dc.trInvS(1)));
} }
} }
......
...@@ -51,6 +51,11 @@ class ValueViewer : public StyleListener { ...@@ -51,6 +51,11 @@ class ValueViewer : public StyleListener {
/// Get a bounding rectangle for this field (including any border it may have) /// Get a bounding rectangle for this field (including any border it may have)
virtual RealRect boundingBox() const; virtual RealRect boundingBox() const;
/// Rotation to use for drawing this field
virtual Rotation getRotation() const;
/// Stretch factor
virtual double getStretch() const { return 1.0; }
/// Called when the associated value is changed /// Called when the associated value is changed
/** Both when we are associated with another value, /** Both when we are associated with another value,
* and by default when the value itself changes (called from onAction) * and by default when the value itself changes (called from onAction)
......
...@@ -113,7 +113,7 @@ template <> void GetDefaultMember::handle(const ScriptableImage& s) { ...@@ -113,7 +113,7 @@ template <> void GetDefaultMember::handle(const ScriptableImage& s) {
void CachedScriptableImage::generateCached(const GeneratedImage::Options& options, void CachedScriptableImage::generateCached(const GeneratedImage::Options& options,
Image* mask, Image* mask,
ImageCombine* combine, wxBitmap* bitmap, wxImage* image) { ImageCombine* combine, wxBitmap* bitmap, wxImage* image, RealSize* size) {
// ready? // ready?
if (!isReady()) { if (!isReady()) {
// error, return blank image // error, return blank image
...@@ -121,20 +121,20 @@ void CachedScriptableImage::generateCached(const GeneratedImage::Options& option ...@@ -121,20 +121,20 @@ void CachedScriptableImage::generateCached(const GeneratedImage::Options& option
i.InitAlpha(); i.InitAlpha();
i.SetAlpha(0,0,0); i.SetAlpha(0,0,0);
*image = i; *image = i;
*size = RealSize(0,0);
return; return;
} }
// find combine mode // find combine mode
ImageCombine combine_i = value->combine(); ImageCombine combine_i = value->combine();
if (combine_i != COMBINE_DEFAULT) *combine = combine_i; if (combine_i != COMBINE_DEFAULT) *combine = combine_i;
// desired size *size = cached_size;
int ow = options.width, oh = options.height; // does the size match?
if (sideways(options.angle)) swap(ow,oh); bool w_ok = cached_size.width == options.width,
h_ok = cached_size.height == options.height;
// image or bitmap? // image or bitmap?
if (*combine <= COMBINE_NORMAL) { if (*combine <= COMBINE_NORMAL) {
// bitmap // bitmap
if (cached_b.Ok() && options.angle == cached_angle) { if (cached_b.Ok() && options.angle == cached_angle) {
bool w_ok = cached_b.GetWidth() == ow,
h_ok = cached_b.GetHeight() == oh;
if ((w_ok && h_ok) || (options.preserve_aspect == ASPECT_FIT && (w_ok || h_ok))) { // only one dimension has to fit when fitting if ((w_ok && h_ok) || (options.preserve_aspect == ASPECT_FIT && (w_ok || h_ok))) { // only one dimension has to fit when fitting
// cached, we are done // cached, we are done
*bitmap = cached_b; *bitmap = cached_b;
...@@ -143,9 +143,7 @@ void CachedScriptableImage::generateCached(const GeneratedImage::Options& option ...@@ -143,9 +143,7 @@ void CachedScriptableImage::generateCached(const GeneratedImage::Options& option
} }
} else { } else {
// image // image
if (cached_i.Ok()) { if (cached_i.Ok() && (options.angle - cached_angle + 360) % 90 == 0) {
bool w_ok = cached_i.GetWidth() == options.width,
h_ok = cached_i.GetHeight() == options.height;
if ((w_ok && h_ok) || (options.preserve_aspect == ASPECT_FIT && (w_ok || h_ok))) { // only one dimension has to fit when fitting if ((w_ok && h_ok) || (options.preserve_aspect == ASPECT_FIT && (w_ok || h_ok))) { // only one dimension has to fit when fitting
if (options.angle != cached_angle) { if (options.angle != cached_angle) {
// rotate cached image // rotate cached image
...@@ -160,6 +158,7 @@ void CachedScriptableImage::generateCached(const GeneratedImage::Options& option ...@@ -160,6 +158,7 @@ void CachedScriptableImage::generateCached(const GeneratedImage::Options& option
// generate // generate
cached_i = generate(options); cached_i = generate(options);
cached_angle = options.angle; cached_angle = options.angle;
*size = cached_size = RealSize(options.width, options.height);
if (mask && mask->Ok()) { if (mask && mask->Ok()) {
// apply mask // apply mask
if (mask->GetWidth() == cached_i.GetWidth() && mask->GetHeight() == cached_i.GetHeight()) { if (mask->GetWidth() == cached_i.GetWidth() && mask->GetHeight() == cached_i.GetHeight()) {
......
...@@ -90,7 +90,7 @@ class CachedScriptableImage : public ScriptableImage { ...@@ -90,7 +90,7 @@ class CachedScriptableImage : public ScriptableImage {
*/ */
void generateCached(const GeneratedImage::Options& img_options, void generateCached(const GeneratedImage::Options& img_options,
Image* mask, Image* mask,
ImageCombine* combine, wxBitmap* bitmap, wxImage* image); ImageCombine* combine, wxBitmap* bitmap, wxImage* image, RealSize* size);
/// Update the script, returns true if the value has changed /// Update the script, returns true if the value has changed
bool update(Context& ctx); bool update(Context& ctx);
...@@ -101,6 +101,7 @@ class CachedScriptableImage : public ScriptableImage { ...@@ -101,6 +101,7 @@ class CachedScriptableImage : public ScriptableImage {
private: private:
Image cached_i; ///< The cached image Image cached_i; ///< The cached image
Bitmap cached_b; ///< *or* the cached bitmap Bitmap cached_b; ///< *or* the cached bitmap
RealSize cached_size; ///< The size of the image before rotating
int cached_angle; int cached_angle;
}; };
......
This diff is collapsed.
...@@ -17,6 +17,11 @@ class Font; ...@@ -17,6 +17,11 @@ class Font;
// ----------------------------------------------------------------------------- : Rotation // ----------------------------------------------------------------------------- : Rotation
enum RotationFlags
{ ROTATION_NORMAL
, ROTATION_ATTACH_TOP_LEFT
};
/// An object that can rotate coordinates inside a specified rectangle /// An object that can rotate coordinates inside a specified rectangle
/** This class has lots of tr*** functions, they convert /** This class has lots of tr*** functions, they convert
* internal coordinates to external/screen coordinates. * internal coordinates to external/screen coordinates.
...@@ -28,48 +33,61 @@ class Rotation { ...@@ -28,48 +33,61 @@ class Rotation {
/** with the given rectangle of external coordinates and a given rotation angle and zoom factor. /** with the given rectangle of external coordinates and a given rotation angle and zoom factor.
* if is_internal then the rect gives the internal coordinates, its origin should be (0,0) * if is_internal then the rect gives the internal coordinates, its origin should be (0,0)
*/ */
Rotation(int angle, const RealRect& rect, double zoom = 1.0, double strectch = 1.0, bool is_internal = false); Rotation(int angle, const RealRect& rect, double zoom = 1.0, double strectch = 1.0, RotationFlags flags = ROTATION_NORMAL);
/// Change the zoom factor /// Change the zoom factor
inline void setZoom(double z) { zoomX = zoomY = z; } inline void setZoom(double z) { zoomX = zoomY = z; }
/// Retrieve the zoom factor /// Retrieve the zoom factor
inline double getZoom() const { return zoomY; } inline double getZoom() const { return zoomY; }
/// Change the angle /// Change the stretch factor
void setAngle(int a); void setStretch(double s);
/// Stretch factor
inline double getStretch() const { return zoomX / zoomY; }
/// Get the angle
inline int getAngle() const { return angle; }
/// Change the origin /// Change the origin
inline void setOrigin(const RealPoint& o) { origin = o; } inline void setOrigin(const RealPoint& o) { origin = o; }
/// The internal size /// The internal size
inline RealSize getInternalSize() const { return trInvNoNeg(size); } inline RealSize getInternalSize() const { return size; }
inline double getWidth() const { return size.width; }
inline double getHeight() const { return size.height; }
/// The intarnal rectangle (origin at (0,0)) /// The intarnal rectangle (origin at (0,0))
inline RealRect getInternalRect() const { return RealRect(RealPoint(0,0), getInternalSize()); } inline RealRect getInternalRect() const { return RealRect(RealPoint(0,0), size); }
/// The size of the external rectangle (as passed to the constructor) == trNoNeg(getInternalSize()) /// The size of the external rectangle (as passed to the constructor) == trNoNeg(getInternalSize())
inline RealSize getExternalSize() const { return size; } inline RealSize getExternalSize() const { return trSizeToBB(size); }
/// The external rectangle (as passed to the constructor) == trNoNeg(getInternalRect()) /// The external rectangle (as passed to the constructor) == trNoNeg(getInternalRect())
RealRect getExternalRect() const; inline RealRect getExternalRect() const { return trRectToBB(getInternalRect()); }
/// Translate a size or length /// Translate a size or length
inline double trS(double s) const { return s * zoomY; } inline double trS(double s) const { return s * zoomY; }
inline double trX(double s) const { return s * zoomX; } inline double trX(double s) const { return s * zoomX; }
inline double trY(double s) const { return s * zoomY; } inline double trY(double s) const { return s * zoomY; }
inline RealSize trS(const RealSize& s) const { return RealSize(s.width * zoomX, s.height * zoomY); }
/// Translate an angle /// Translate an angle
inline int trAngle(int a) { return (angle + a) % 360; } inline int trAngle(int a) { return (angle + a) % 360; }
/// Translate a single point /// Translate a single point
RealPoint tr(const RealPoint& p) const; RealPoint tr(const RealPoint& p) const;
/// Translate a single size, the result may be negative /// Translate a single point, but don't zoom
RealSize tr(const RealSize& s) const; RealPoint trNoZoom(const RealPoint& p) const;
/// Translate a rectangle, the size of the result may be negative /// Translate a 'pixel'. A pixel has size 1*1
RealRect tr(const RealRect& r) const; RealPoint trPixel(const RealPoint& p) const;
/// Translate a 'pixel', but don't zoom
/// Translate a size, the result will never be negative RealPoint trPixelNoZoom(const RealPoint& p) const;
RealSize trNoNeg(const RealSize& s) const; /// Translate a single size
/// Translate a rectangle, the result will never have a negative size RealSize trSize(const RealSize& s) const;
RealRect trNoNeg(const RealRect& r) const; /// Translate a single size, returns the bounding box size (non-negative)
/// Translate a rectangle, the result will never have a negative size RealSize trSizeToBB(const RealSize& s) const;
/** The rectangle is also not zoomed */ /// Translate a rectangle, returns the bounding box
RealRect trNoNegNoZoom(const RealRect& r) const; /* //%%the size of the result may be negative*/
RealRect trRectToBB(const RealRect& r) const;
/// Translate a rectangle, can only be used when not rotating
RealRect trRectStraight(const RealRect& r) const;
/// Translate a rectangle into a region (supports rotation
wxRegion trRectToRegion(const RealRect& rect) const;
/// Translate a size or length back to internal 'coordinates' /// Translate a size or length back to internal 'coordinates'
inline double trInvS(double s) const { return s / zoomY; } inline double trInvS(double s) const { return s / zoomY; }
...@@ -80,36 +98,28 @@ class Rotation { ...@@ -80,36 +98,28 @@ class Rotation {
/// Translate a point back to internal coordinates /// Translate a point back to internal coordinates
RealPoint trInv(const RealPoint& p) const; RealPoint trInv(const RealPoint& p) const;
/// Translate a size back to internal coordinates
RealSize trInv(const RealSize& s) const;
/// Translate a size back to internal coordinates, that are not negative
RealSize trInvNoNeg(const RealSize& s) const;
/// Stretch factor
inline double stretch() const { return zoomX / zoomY; }
protected: protected:
int angle; ///< The angle of rotation in degrees (counterclockwise) int angle; ///< The angle of rotation in degrees (counterclockwise)
RealSize size; ///< Size of the rectangle, in external coordinates RealSize size; ///< Size of the rectangle, in internal coordinates
RealPoint origin; ///< tr(0,0) RealPoint origin; ///< tr(0,0)
double zoomX; ///< Zoom factor, zoom = 2.0 means that 1 internal = 2 external double zoomX; ///< Zoom factor, zoom = 2.0 means that 1 internal = 2 external
double zoomY; double zoomY;
friend class Rotater; friend class Rotater;
public:
/// Is the rotation sideways (90 or 270 degrees)?
inline bool sideways() const { return ::sideways(angle); }
protected:
/// Is the x axis 'reversed' (after turning sideways)? /// Is the x axis 'reversed' (after turning sideways)?
inline bool revX() const { return angle >= 180; } inline bool revX() const { return angle >= 180; }
/// Is the y axis 'reversed' (after turning sideways)? /// Is the y axis 'reversed' (after turning sideways)?
inline bool revY() const { return angle == 90 || angle == 180; } inline bool revY() const { return angle == 90 || angle == 180; }
/// Negate if revX /// Is the rotation 'simple', i.e. a multiple of 90 degrees?
inline double negX(double d) const { return revX() ? -d : d; } inline bool straight() const { return ::straight(angle); }
/// Negate if revY /// Is the rotation sideways (90 or 270 degrees)?
inline double negY(double d) const { return revY() ? -d : d; } // Note: angle & 2 == 0 for angle in {0, 180} and != 0 for angle in {90, 270)
inline bool sideways() const { return (angle & 2) != 0; }
/// Determine the top-left corner of the bounding box around the rotated box s (in external coordinates)
RealPoint boundingBoxCorner(const RealSize& s) const;
}; };
// ----------------------------------------------------------------------------- : Rotater // ----------------------------------------------------------------------------- : Rotater
...@@ -138,9 +148,9 @@ class Rotater { ...@@ -138,9 +148,9 @@ class Rotater {
/// Render quality of text /// Render quality of text
enum RenderQuality { enum RenderQuality {
QUALITY_AA, ///< Our own anti aliassing
QUALITY_SUB_PIXEL, ///< Sub-pixel positioning
QUALITY_LOW, ///< Normal QUALITY_LOW, ///< Normal
QUALITY_SUB_PIXEL, ///< Sub-pixel positioning
QUALITY_AA, ///< Our own anti aliassing
}; };
/// A DC with rotation applied /// A DC with rotation applied
...@@ -148,20 +158,21 @@ enum RenderQuality { ...@@ -148,20 +158,21 @@ enum RenderQuality {
*/ */
class RotatedDC : public Rotation { class RotatedDC : public Rotation {
public: public:
RotatedDC(DC& dc, int angle, const RealRect& rect, double zoom, RenderQuality quality, bool is_internal = false); RotatedDC(DC& dc, int angle, const RealRect& rect, double zoom, RenderQuality quality, RotationFlags flags = ROTATION_NORMAL);
RotatedDC(DC& dc, const Rotation& rotation, RenderQuality quality); RotatedDC(DC& dc, const Rotation& rotation, RenderQuality quality);
// --------------------------------------------------- : Drawing // --------------------------------------------------- : Drawing
void DrawText (const String& text, const RealPoint& pos, int blur_radius = 0, int boldness = 1, double stretch = 1.0); void DrawText (const String& text, const RealPoint& pos, int blur_radius = 0, int boldness = 1, double stretch = 1.0);
/// Draw abitmap, it must already be zoomed! /// Draw abitmap, it must already be zoomed!
void DrawBitmap(const Bitmap& bitmap, const RealPoint& pos); void DrawBitmap(const Bitmap& bitmap, const RealPoint& pos);
/// Draw an image using the given combining mode, the image must already be zoomed! /// Draw an image using the given combining mode, the image must already be zoomed!
void DrawImage (const Image& image, const RealPoint& pos, ImageCombine combine = COMBINE_DEFAULT, int angle = 0); void DrawImage (const Image& image, const RealPoint& pos, ImageCombine combine = COMBINE_DEFAULT);
/// Draw a bitmap that is already zoomed and rotated /// Draw a bitmap that is already zoomed and rotated.
void DrawPreRotatedBitmap(const Bitmap& bitmap, const RealPoint& pos); /** The rectangle the position in internal coordinates, and the size before rotating and zooming */
void DrawPreRotatedBitmap(const Bitmap& bitmap, const RealRect& rect);
/// Draw an image that is already zoomed and rotated /// Draw an image that is already zoomed and rotated
void DrawPreRotatedImage(const Image& image, const RealPoint& pos, ImageCombine combine = COMBINE_DEFAULT); void DrawPreRotatedImage(const Image& image, const RealRect& rect, ImageCombine combine = COMBINE_DEFAULT);
void DrawLine (const RealPoint& p1, const RealPoint& p2); void DrawLine (const RealPoint& p1, const RealPoint& p2);
void DrawRectangle(const RealRect& r); void DrawRectangle(const RealRect& r);
void DrawRoundedRectangle(const RealRect& r, double radius); void DrawRoundedRectangle(const RealRect& r, double radius);
......
...@@ -49,7 +49,7 @@ template <> void GetDefaultMember::handle(const Version& v) { ...@@ -49,7 +49,7 @@ template <> void GetDefaultMember::handle(const Version& v) {
// ----------------------------------------------------------------------------- : Versions // ----------------------------------------------------------------------------- : Versions
// NOTE: Don't use leading zeroes, they mean octal // NOTE: Don't use leading zeroes, they mean octal
const Version app_version = 305; // 0.3.5 const Version app_version = 306; // 0.3.6
#ifdef UNICODE #ifdef UNICODE
const Char* version_suffix = _(" (beta)"); const Char* version_suffix = _(" (beta)");
#else #else
...@@ -67,5 +67,6 @@ const Char* version_suffix = _(" (beta, ascii build)"); ...@@ -67,5 +67,6 @@ const Char* version_suffix = _(" (beta, ascii build)");
* 0.3.3 : keyword separator before/after * 0.3.3 : keyword separator before/after
* 0.3.4 : html export; choice rendering based on scripted 'image' * 0.3.4 : html export; choice rendering based on scripted 'image'
* 0.3.5 : word lists, symbol font 'as text' * 0.3.5 : word lists, symbol font 'as text'
* 0.3.6 : free rotation, rotation behaviour changed.
*/ */
const Version file_version = 305; // 0.3.5 const Version file_version = 306; // 0.3.6
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment