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