mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-11 21:27:01 -04:00
Change tabs to two spaces.
This commit is contained in:
+156
-156
@@ -31,219 +31,219 @@ DataViewer::~DataViewer() {}
|
||||
IMPLEMENT_DYNAMIC_ARG(bool, drawing_card, false);
|
||||
|
||||
void DataViewer::draw(DC& dc) {
|
||||
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet);
|
||||
RotatedDC rdc(dc, getRotation(),
|
||||
nativeLook() ? QUALITY_LOW : (ss.card_anti_alias() ? QUALITY_AA : QUALITY_SUB_PIXEL));
|
||||
draw(rdc, stylesheet->card_background);
|
||||
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet);
|
||||
RotatedDC rdc(dc, getRotation(),
|
||||
nativeLook() ? QUALITY_LOW : (ss.card_anti_alias() ? QUALITY_AA : QUALITY_SUB_PIXEL));
|
||||
draw(rdc, stylesheet->card_background);
|
||||
}
|
||||
void DataViewer::draw(RotatedDC& dc, const Color& background) {
|
||||
if (!set) return; // no set specified, don't draw anything
|
||||
WITH_DYNAMIC_ARG(drawing_card, true);
|
||||
// fill with background color
|
||||
clearDC(dc.getDC(), background);
|
||||
// update style scripts
|
||||
updateStyles(false);
|
||||
// prepare viewers
|
||||
bool changed_content_properties = false;
|
||||
FOR_EACH(v, viewers) { // draw low z index fields first
|
||||
if (v->getStyle()->isVisible()) {
|
||||
Rotater r(dc, v->getRotation());
|
||||
try {
|
||||
if (v->prepare(dc)) {
|
||||
changed_content_properties = true;
|
||||
}
|
||||
} catch (const Error& e) {
|
||||
handle_error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changed_content_properties) {
|
||||
updateStyles(true);
|
||||
}
|
||||
// draw viewers
|
||||
FOR_EACH(v, viewers) { // draw low z index fields first
|
||||
if (v->getStyle()->isVisible()) {// visible
|
||||
Rotater r(dc, v->getRotation());
|
||||
try {
|
||||
drawViewer(dc, *v);
|
||||
} catch (const Error& e) {
|
||||
handle_error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!set) return; // no set specified, don't draw anything
|
||||
WITH_DYNAMIC_ARG(drawing_card, true);
|
||||
// fill with background color
|
||||
clearDC(dc.getDC(), background);
|
||||
// update style scripts
|
||||
updateStyles(false);
|
||||
// prepare viewers
|
||||
bool changed_content_properties = false;
|
||||
FOR_EACH(v, viewers) { // draw low z index fields first
|
||||
if (v->getStyle()->isVisible()) {
|
||||
Rotater r(dc, v->getRotation());
|
||||
try {
|
||||
if (v->prepare(dc)) {
|
||||
changed_content_properties = true;
|
||||
}
|
||||
} catch (const Error& e) {
|
||||
handle_error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changed_content_properties) {
|
||||
updateStyles(true);
|
||||
}
|
||||
// draw viewers
|
||||
FOR_EACH(v, viewers) { // draw low z index fields first
|
||||
if (v->getStyle()->isVisible()) {// visible
|
||||
Rotater r(dc, v->getRotation());
|
||||
try {
|
||||
drawViewer(dc, *v);
|
||||
} catch (const Error& e) {
|
||||
handle_error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void DataViewer::drawViewer(RotatedDC& dc, ValueViewer& v) {
|
||||
v.draw(dc);
|
||||
v.draw(dc);
|
||||
}
|
||||
|
||||
void DataViewer::updateStyles(bool only_content_dependent) {
|
||||
try {
|
||||
if (card) {
|
||||
set->updateStyles(card, only_content_dependent);
|
||||
} else {
|
||||
Context& ctx = getContext();
|
||||
FOR_EACH(v, viewers) {
|
||||
Style& s = *v->getStyle();
|
||||
if (only_content_dependent && !s.content_dependent) continue;
|
||||
if (s.update(ctx)) {
|
||||
s.tellListeners(only_content_dependent);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const Error& e) {
|
||||
handle_error(e);
|
||||
}
|
||||
try {
|
||||
if (card) {
|
||||
set->updateStyles(card, only_content_dependent);
|
||||
} else {
|
||||
Context& ctx = getContext();
|
||||
FOR_EACH(v, viewers) {
|
||||
Style& s = *v->getStyle();
|
||||
if (only_content_dependent && !s.content_dependent) continue;
|
||||
if (s.update(ctx)) {
|
||||
s.tellListeners(only_content_dependent);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const Error& e) {
|
||||
handle_error(e);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Utility for ValueViewers
|
||||
|
||||
bool DataViewer::nativeLook() const {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
DrawWhat DataViewer::drawWhat(const ValueViewer*) const {
|
||||
return (DrawWhat)(DRAW_NORMAL | nativeLook() * DRAW_NATIVELOOK);
|
||||
return (DrawWhat)(DRAW_NORMAL | nativeLook() * DRAW_NATIVELOOK);
|
||||
}
|
||||
|
||||
bool DataViewer::viewerIsCurrent(const ValueViewer*) const {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
Context& DataViewer::getContext() const {
|
||||
return set->getContext(card);
|
||||
return set->getContext(card);
|
||||
}
|
||||
|
||||
Rotation DataViewer::getRotation() const {
|
||||
if (!stylesheet) stylesheet = set->stylesheet;
|
||||
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet);
|
||||
return Rotation(deg_to_rad(ss.card_angle()), stylesheet->getCardRect(), ss.card_zoom(), 1.0, ROTATION_ATTACH_TOP_LEFT);
|
||||
if (!stylesheet) stylesheet = set->stylesheet;
|
||||
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet);
|
||||
return Rotation(deg_to_rad(ss.card_angle()), stylesheet->getCardRect(), ss.card_zoom(), 1.0, ROTATION_ATTACH_TOP_LEFT);
|
||||
}
|
||||
|
||||
Package& DataViewer::getStylePackage() const {
|
||||
return *stylesheet;
|
||||
return *stylesheet;
|
||||
}
|
||||
Package& DataViewer::getLocalPackage() const {
|
||||
return *set;
|
||||
return *set;
|
||||
}
|
||||
Game& DataViewer::getGame() const {
|
||||
return *set->game;
|
||||
return *set->game;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Setting data
|
||||
|
||||
void DataViewer::setCard(const CardP& card, bool refresh) {
|
||||
if (!card) return; // TODO: clear viewer?
|
||||
StyleSheetP new_stylesheet = set->stylesheetForP(card);
|
||||
if (!refresh && this->card == card && this->stylesheet == new_stylesheet) return; // already set
|
||||
assert(set);
|
||||
this->card = card;
|
||||
stylesheet = new_stylesheet;
|
||||
setStyles(stylesheet, stylesheet->card_style, &stylesheet->extra_card_style);
|
||||
setData(card->data, &card->extraDataFor(*stylesheet));
|
||||
onChangeSize();
|
||||
if (!card) return; // TODO: clear viewer?
|
||||
StyleSheetP new_stylesheet = set->stylesheetForP(card);
|
||||
if (!refresh && this->card == card && this->stylesheet == new_stylesheet) return; // already set
|
||||
assert(set);
|
||||
this->card = card;
|
||||
stylesheet = new_stylesheet;
|
||||
setStyles(stylesheet, stylesheet->card_style, &stylesheet->extra_card_style);
|
||||
setData(card->data, &card->extraDataFor(*stylesheet));
|
||||
onChangeSize();
|
||||
}
|
||||
|
||||
void DataViewer::onChangeSet() {
|
||||
viewers.clear();
|
||||
onInit();
|
||||
onChange();
|
||||
onChangeSize();
|
||||
viewers.clear();
|
||||
onInit();
|
||||
onChange();
|
||||
onChangeSize();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Viewers
|
||||
|
||||
struct CompareViewer {
|
||||
bool operator() (const ValueViewerP& a, const ValueViewerP& b) {
|
||||
return a->getStyle()->z_index < b->getStyle()->z_index;
|
||||
}
|
||||
bool operator() (const ValueViewerP& a, const ValueViewerP& b) {
|
||||
return a->getStyle()->z_index < b->getStyle()->z_index;
|
||||
}
|
||||
};
|
||||
|
||||
void DataViewer::setStyles(const StyleSheetP& stylesheet, IndexMap<FieldP,StyleP>& styles, IndexMap<FieldP,StyleP>* extra_styles) {
|
||||
if (!viewers.empty() && styles.contains(viewers.front()->getStyle())) {
|
||||
// already using these styles
|
||||
return;
|
||||
}
|
||||
this->stylesheet = stylesheet;
|
||||
// create viewers
|
||||
viewers.clear();
|
||||
addStyles(styles);
|
||||
if (extra_styles) addStyles(*extra_styles);
|
||||
// sort viewers by z-index of style
|
||||
stable_sort(viewers.begin(), viewers.end(), CompareViewer());
|
||||
onInit();
|
||||
if (!viewers.empty() && styles.contains(viewers.front()->getStyle())) {
|
||||
// already using these styles
|
||||
return;
|
||||
}
|
||||
this->stylesheet = stylesheet;
|
||||
// create viewers
|
||||
viewers.clear();
|
||||
addStyles(styles);
|
||||
if (extra_styles) addStyles(*extra_styles);
|
||||
// sort viewers by z-index of style
|
||||
stable_sort(viewers.begin(), viewers.end(), CompareViewer());
|
||||
onInit();
|
||||
}
|
||||
void DataViewer::addStyles(IndexMap<FieldP,StyleP>& styles) {
|
||||
FOR_EACH(s, styles) {
|
||||
if ((s->visible || s->visible.isScripted()) && (nativeLook() || s->hasSize())) {
|
||||
// no need to make a viewer for things that are always invisible
|
||||
ValueViewerP viewer = makeViewer(s);
|
||||
if (viewer) viewers.push_back(viewer);
|
||||
}
|
||||
}
|
||||
FOR_EACH(s, styles) {
|
||||
if ((s->visible || s->visible.isScripted()) && (nativeLook() || s->hasSize())) {
|
||||
// no need to make a viewer for things that are always invisible
|
||||
ValueViewerP viewer = makeViewer(s);
|
||||
if (viewer) viewers.push_back(viewer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DataViewer::setData(IndexMap<FieldP,ValueP>& values, IndexMap<FieldP,ValueP>* extra_values) {
|
||||
FOR_EACH(v, viewers) {
|
||||
// is this field contained in values?
|
||||
ValueP val = values.tryGet(v->getField());
|
||||
if (val) {
|
||||
v->setValue(val);
|
||||
} else {
|
||||
// if it is not in values it should be in extra values
|
||||
assert(extra_values);
|
||||
val = extra_values->tryGet(v->getField());
|
||||
assert(val);
|
||||
v->setValue(val);
|
||||
}
|
||||
}
|
||||
onChange();
|
||||
FOR_EACH(v, viewers) {
|
||||
// is this field contained in values?
|
||||
ValueP val = values.tryGet(v->getField());
|
||||
if (val) {
|
||||
v->setValue(val);
|
||||
} else {
|
||||
// if it is not in values it should be in extra values
|
||||
assert(extra_values);
|
||||
val = extra_values->tryGet(v->getField());
|
||||
assert(val);
|
||||
v->setValue(val);
|
||||
}
|
||||
}
|
||||
onChange();
|
||||
}
|
||||
|
||||
|
||||
ValueViewerP DataViewer::makeViewer(const StyleP& style) {
|
||||
return style->makeViewer(*this, style);
|
||||
return style->makeViewer(*this, style);
|
||||
}
|
||||
|
||||
void DataViewer::onAction(const Action& action, bool undone) {
|
||||
TYPE_CASE_(action, DisplayChangeAction) {
|
||||
// refresh
|
||||
setCard(card, true);
|
||||
return;
|
||||
}
|
||||
TYPE_CASE(action, ValueAction) {
|
||||
if (action.card == card.get()) {
|
||||
FOR_EACH(v, viewers) {
|
||||
if (v->getValue()->equals( action.valueP.get() )) {
|
||||
// refresh the viewer
|
||||
v->onAction(action, undone);
|
||||
onChange();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TYPE_CASE(action, ScriptValueEvent) {
|
||||
if (action.card == card.get()) {
|
||||
FOR_EACH(v, viewers) {
|
||||
if (v->getValue().get() == action.value) {
|
||||
// refresh the viewer
|
||||
v->onAction(action, undone);
|
||||
onChange();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*//% TYPE_CASE(action, ScriptStyleEvent) {
|
||||
if (action.stylesheet == stylesheet.get()) {
|
||||
FOR_EACH(v, viewers) {
|
||||
if (v->getStyle().get() == action.style) {
|
||||
// refresh the viewer
|
||||
v->onStyleChange();
|
||||
if (!drawing) onChange();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
TYPE_CASE_(action, DisplayChangeAction) {
|
||||
// refresh
|
||||
setCard(card, true);
|
||||
return;
|
||||
}
|
||||
TYPE_CASE(action, ValueAction) {
|
||||
if (action.card == card.get()) {
|
||||
FOR_EACH(v, viewers) {
|
||||
if (v->getValue()->equals( action.valueP.get() )) {
|
||||
// refresh the viewer
|
||||
v->onAction(action, undone);
|
||||
onChange();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TYPE_CASE(action, ScriptValueEvent) {
|
||||
if (action.card == card.get()) {
|
||||
FOR_EACH(v, viewers) {
|
||||
if (v->getValue().get() == action.value) {
|
||||
// refresh the viewer
|
||||
v->onAction(action, undone);
|
||||
onChange();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*//% TYPE_CASE(action, ScriptStyleEvent) {
|
||||
if (action.stylesheet == stylesheet.get()) {
|
||||
FOR_EACH(v, viewers) {
|
||||
if (v->getStyle().get() == action.style) {
|
||||
// refresh the viewer
|
||||
v->onStyleChange();
|
||||
if (!drawing) onChange();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
+75
-75
@@ -24,83 +24,83 @@ DECLARE_DYNAMIC_ARG(bool, drawing_card);
|
||||
|
||||
/// A viewer can generate an image of some values, usually a card.
|
||||
class DataViewer : public SetView {
|
||||
public:
|
||||
DataViewer();
|
||||
~DataViewer();
|
||||
|
||||
// --------------------------------------------------- : Drawing
|
||||
|
||||
/// Draw the current (card/data) to the given dc
|
||||
virtual void draw(DC& dc);
|
||||
/// Draw the current (card/data) to the given dc
|
||||
virtual void draw(RotatedDC& dc, const Color& background);
|
||||
/// Draw a single viewer
|
||||
virtual void drawViewer(RotatedDC& dc, ValueViewer& v);
|
||||
|
||||
// --------------------------------------------------- : Utility for ValueViewers
|
||||
|
||||
/// Should the ValueViewers use a platform native look and feel?
|
||||
/** false by default, can be overloaded */
|
||||
virtual bool nativeLook() const;
|
||||
/// Which things should be drawn for the given viewer?
|
||||
/** can be overloaded */
|
||||
virtual DrawWhat drawWhat(const ValueViewer*) const;
|
||||
/// Is the given viewer currently selected?
|
||||
virtual bool viewerIsCurrent(const ValueViewer*) const;
|
||||
/// Get a script context to use for scripts in the viewers
|
||||
Context& getContext() const;
|
||||
/// The rotation to use
|
||||
virtual Rotation getRotation() const;
|
||||
/// The card we are viewing, can be null
|
||||
inline const CardP& getCard() const { return card; }
|
||||
/// Invalidate and redraw (the area of) a single value viewer
|
||||
virtual void redraw(const ValueViewer&) {}
|
||||
|
||||
/// The package containing style stuff like images
|
||||
virtual Package& getStylePackage() const;
|
||||
/// The local package for loading/saving files
|
||||
Package& getLocalPackage() const;
|
||||
/// Return the game to use for information
|
||||
Game& getGame() const;
|
||||
|
||||
// --------------------------------------------------- : Setting data
|
||||
|
||||
/// Display a card in this viewer
|
||||
/** \param refresh: Always refresh, even if this card is already shown */
|
||||
void setCard(const CardP& card, bool refresh = false);
|
||||
|
||||
/// Clear data
|
||||
virtual void onChangeSet();
|
||||
|
||||
// --------------------------------------------------- : The viewers
|
||||
public:
|
||||
DataViewer();
|
||||
~DataViewer();
|
||||
|
||||
// --------------------------------------------------- : Drawing
|
||||
|
||||
/// Draw the current (card/data) to the given dc
|
||||
virtual void draw(DC& dc);
|
||||
/// Draw the current (card/data) to the given dc
|
||||
virtual void draw(RotatedDC& dc, const Color& background);
|
||||
/// Draw a single viewer
|
||||
virtual void drawViewer(RotatedDC& dc, ValueViewer& v);
|
||||
|
||||
// --------------------------------------------------- : Utility for ValueViewers
|
||||
|
||||
/// Should the ValueViewers use a platform native look and feel?
|
||||
/** false by default, can be overloaded */
|
||||
virtual bool nativeLook() const;
|
||||
/// Which things should be drawn for the given viewer?
|
||||
/** can be overloaded */
|
||||
virtual DrawWhat drawWhat(const ValueViewer*) const;
|
||||
/// Is the given viewer currently selected?
|
||||
virtual bool viewerIsCurrent(const ValueViewer*) const;
|
||||
/// Get a script context to use for scripts in the viewers
|
||||
Context& getContext() const;
|
||||
/// The rotation to use
|
||||
virtual Rotation getRotation() const;
|
||||
/// The card we are viewing, can be null
|
||||
inline const CardP& getCard() const { return card; }
|
||||
/// Invalidate and redraw (the area of) a single value viewer
|
||||
virtual void redraw(const ValueViewer&) {}
|
||||
|
||||
/// The package containing style stuff like images
|
||||
virtual Package& getStylePackage() const;
|
||||
/// The local package for loading/saving files
|
||||
Package& getLocalPackage() const;
|
||||
/// Return the game to use for information
|
||||
Game& getGame() const;
|
||||
|
||||
// --------------------------------------------------- : Setting data
|
||||
|
||||
/// Display a card in this viewer
|
||||
/** \param refresh: Always refresh, even if this card is already shown */
|
||||
void setCard(const CardP& card, bool refresh = false);
|
||||
|
||||
/// Clear data
|
||||
virtual void onChangeSet();
|
||||
|
||||
// --------------------------------------------------- : The viewers
|
||||
private:
|
||||
/// Create some viewers for the given styles
|
||||
void addStyles(IndexMap<FieldP,StyleP>& styles);
|
||||
/// Update style scripts
|
||||
void updateStyles(bool only_content_dependent);
|
||||
/// Create some viewers for the given styles
|
||||
void addStyles(IndexMap<FieldP,StyleP>& styles);
|
||||
/// Update style scripts
|
||||
void updateStyles(bool only_content_dependent);
|
||||
protected:
|
||||
/// Set the styles for the data to be shown, recreating the viewers
|
||||
void setStyles(const StyleSheetP& stylesheet, IndexMap<FieldP,StyleP>& styles, IndexMap<FieldP,StyleP>* extra_styles = nullptr);
|
||||
/// Set the data to be shown in the viewers, refresh them
|
||||
void setData(IndexMap<FieldP,ValueP>& values, IndexMap<FieldP,ValueP>* extra_values = nullptr);
|
||||
|
||||
/// Create a viewer for the given style.
|
||||
/** Can be overloaded to create a ValueEditor instead */
|
||||
virtual ValueViewerP makeViewer(const StyleP&);
|
||||
|
||||
/// Update the viewers and forward actions
|
||||
virtual void onAction(const Action&, bool undone);
|
||||
|
||||
/// Notification that the total image has changed
|
||||
virtual void onChange() {}
|
||||
/// Notification that the viewers are initialized
|
||||
virtual void onInit() {}
|
||||
/// Notification that the size of the viewer may have changed
|
||||
virtual void onChangeSize() {}
|
||||
|
||||
vector<ValueViewerP> viewers; ///< The viewers for the different values in the data
|
||||
CardP card; ///< The card that is currently displayed, if any
|
||||
mutable StyleSheetP stylesheet; ///< Stylesheet being used
|
||||
/// Set the styles for the data to be shown, recreating the viewers
|
||||
void setStyles(const StyleSheetP& stylesheet, IndexMap<FieldP,StyleP>& styles, IndexMap<FieldP,StyleP>* extra_styles = nullptr);
|
||||
/// Set the data to be shown in the viewers, refresh them
|
||||
void setData(IndexMap<FieldP,ValueP>& values, IndexMap<FieldP,ValueP>* extra_values = nullptr);
|
||||
|
||||
/// Create a viewer for the given style.
|
||||
/** Can be overloaded to create a ValueEditor instead */
|
||||
virtual ValueViewerP makeViewer(const StyleP&);
|
||||
|
||||
/// Update the viewers and forward actions
|
||||
virtual void onAction(const Action&, bool undone);
|
||||
|
||||
/// Notification that the total image has changed
|
||||
virtual void onChange() {}
|
||||
/// Notification that the viewers are initialized
|
||||
virtual void onInit() {}
|
||||
/// Notification that the size of the viewer may have changed
|
||||
virtual void onChangeSize() {}
|
||||
|
||||
vector<ValueViewerP> viewers; ///< The viewers for the different values in the data
|
||||
CardP card; ///< The card that is currently displayed, if any
|
||||
mutable StyleSheetP stylesheet; ///< Stylesheet being used
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
@@ -15,72 +15,72 @@
|
||||
// ----------------------------------------------------------------------------- : Symbol filtering
|
||||
|
||||
void filter_symbol(Image& symbol, const SymbolFilter& filter) {
|
||||
Byte* data = symbol.GetData();
|
||||
Byte* alpha = symbol.GetAlpha();
|
||||
UInt width = symbol.GetWidth(), height = symbol.GetHeight();
|
||||
// HACK: wxGTK seems to fail sometimes if you ask it to allocate the alpha channel.
|
||||
// This manually allocates the memory and gives it to the image to handle.
|
||||
if (!alpha) {
|
||||
alpha = (Byte*) malloc(width * height);
|
||||
symbol.SetAlpha(alpha);
|
||||
}
|
||||
for (UInt y = 0 ; y < height ; ++y) {
|
||||
for (UInt x = 0 ; x < width ; ++x) {
|
||||
// Determine set
|
||||
// green -> border or outside
|
||||
// green+red=white -> border
|
||||
if (data[0] != data[2]) {
|
||||
// yellow/blue = editing hint, leave alone
|
||||
} else {
|
||||
SymbolSet point = data[1] ? (data[0] ? SYMBOL_BORDER : SYMBOL_OUTSIDE) : SYMBOL_INSIDE;
|
||||
// Call filter
|
||||
AColor result = filter.color((double)x / width, (double)y / height, point);
|
||||
// Store color
|
||||
data[0] = result.Red();
|
||||
data[1] = result.Green();
|
||||
data[2] = result.Blue();
|
||||
alpha[0] = result.alpha;
|
||||
}
|
||||
// next
|
||||
data += 3;
|
||||
alpha += 1;
|
||||
}
|
||||
}
|
||||
Byte* data = symbol.GetData();
|
||||
Byte* alpha = symbol.GetAlpha();
|
||||
UInt width = symbol.GetWidth(), height = symbol.GetHeight();
|
||||
// HACK: wxGTK seems to fail sometimes if you ask it to allocate the alpha channel.
|
||||
// This manually allocates the memory and gives it to the image to handle.
|
||||
if (!alpha) {
|
||||
alpha = (Byte*) malloc(width * height);
|
||||
symbol.SetAlpha(alpha);
|
||||
}
|
||||
for (UInt y = 0 ; y < height ; ++y) {
|
||||
for (UInt x = 0 ; x < width ; ++x) {
|
||||
// Determine set
|
||||
// green -> border or outside
|
||||
// green+red=white -> border
|
||||
if (data[0] != data[2]) {
|
||||
// yellow/blue = editing hint, leave alone
|
||||
} else {
|
||||
SymbolSet point = data[1] ? (data[0] ? SYMBOL_BORDER : SYMBOL_OUTSIDE) : SYMBOL_INSIDE;
|
||||
// Call filter
|
||||
AColor result = filter.color((double)x / width, (double)y / height, point);
|
||||
// Store color
|
||||
data[0] = result.Red();
|
||||
data[1] = result.Green();
|
||||
data[2] = result.Blue();
|
||||
alpha[0] = result.alpha;
|
||||
}
|
||||
// next
|
||||
data += 3;
|
||||
alpha += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image render_symbol(const SymbolP& symbol, const SymbolFilter& filter, double border_radius, int width, int height, bool edit_hints, bool allow_smaller) {
|
||||
Image i = render_symbol(symbol, border_radius, width, height, edit_hints, allow_smaller);
|
||||
filter_symbol(i, filter);
|
||||
return i;
|
||||
Image i = render_symbol(symbol, border_radius, width, height, edit_hints, allow_smaller);
|
||||
filter_symbol(i, filter);
|
||||
return i;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : SymbolFilter
|
||||
|
||||
IMPLEMENT_REFLECTION_NO_SCRIPT(SymbolFilter) {
|
||||
REFLECT_IF_NOT_READING {
|
||||
String fill_type = fillType();
|
||||
REFLECT(fill_type);
|
||||
}
|
||||
REFLECT_IF_NOT_READING {
|
||||
String fill_type = fillType();
|
||||
REFLECT(fill_type);
|
||||
}
|
||||
}
|
||||
template <> void GetMember::handle(const intrusive_ptr<SymbolFilter>& f) {
|
||||
handle(*f);
|
||||
handle(*f);
|
||||
}
|
||||
|
||||
template <>
|
||||
intrusive_ptr<SymbolFilter> read_new<SymbolFilter>(Reader& reader) {
|
||||
// there must be a fill type specified
|
||||
String fill_type;
|
||||
reader.handle(_("fill type"), fill_type);
|
||||
if (fill_type == _("solid")) return intrusive(new SolidFillSymbolFilter);
|
||||
else if (fill_type == _("linear gradient")) return intrusive(new LinearGradientSymbolFilter);
|
||||
else if (fill_type == _("radial gradient")) return intrusive(new RadialGradientSymbolFilter);
|
||||
else if (fill_type.empty()) {
|
||||
reader.warning(_ERROR_1_("expected key", _("fill type")));
|
||||
throw ParseError(_ERROR_("aborting parsing"));
|
||||
} else {
|
||||
reader.warning(_ERROR_1_("unsupported fill type", fill_type));
|
||||
throw ParseError(_ERROR_("aborting parsing"));
|
||||
}
|
||||
// there must be a fill type specified
|
||||
String fill_type;
|
||||
reader.handle(_("fill type"), fill_type);
|
||||
if (fill_type == _("solid")) return intrusive(new SolidFillSymbolFilter);
|
||||
else if (fill_type == _("linear gradient")) return intrusive(new LinearGradientSymbolFilter);
|
||||
else if (fill_type == _("radial gradient")) return intrusive(new RadialGradientSymbolFilter);
|
||||
else if (fill_type.empty()) {
|
||||
reader.warning(_ERROR_1_("expected key", _("fill type")));
|
||||
throw ParseError(_ERROR_("aborting parsing"));
|
||||
} else {
|
||||
reader.warning(_ERROR_1_("unsupported fill type", fill_type));
|
||||
throw ParseError(_ERROR_("aborting parsing"));
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : SolidFillSymbolFilter
|
||||
@@ -88,45 +88,45 @@ intrusive_ptr<SymbolFilter> read_new<SymbolFilter>(Reader& reader) {
|
||||
String SolidFillSymbolFilter::fillType() const { return _("solid"); }
|
||||
|
||||
AColor SolidFillSymbolFilter::color(double x, double y, SymbolSet point) const {
|
||||
if (point == SYMBOL_INSIDE) return fill_color;
|
||||
else if (point == SYMBOL_BORDER) return border_color;
|
||||
else return AColor(0,0,0,0);
|
||||
if (point == SYMBOL_INSIDE) return fill_color;
|
||||
else if (point == SYMBOL_BORDER) return border_color;
|
||||
else return AColor(0,0,0,0);
|
||||
}
|
||||
|
||||
bool SolidFillSymbolFilter::operator == (const SymbolFilter& that) const {
|
||||
const SolidFillSymbolFilter* that2 = dynamic_cast<const SolidFillSymbolFilter*>(&that);
|
||||
return that2 && fill_color == that2->fill_color
|
||||
&& border_color == that2->border_color;
|
||||
const SolidFillSymbolFilter* that2 = dynamic_cast<const SolidFillSymbolFilter*>(&that);
|
||||
return that2 && fill_color == that2->fill_color
|
||||
&& border_color == that2->border_color;
|
||||
}
|
||||
|
||||
IMPLEMENT_REFLECTION(SolidFillSymbolFilter) {
|
||||
REFLECT_BASE(SymbolFilter);
|
||||
REFLECT(fill_color);
|
||||
REFLECT(border_color);
|
||||
REFLECT_BASE(SymbolFilter);
|
||||
REFLECT(fill_color);
|
||||
REFLECT(border_color);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : GradientSymbolFilter
|
||||
|
||||
template <typename T>
|
||||
AColor GradientSymbolFilter::color(double x, double y, SymbolSet point, const T* t) const {
|
||||
if (point == SYMBOL_INSIDE) return lerp(fill_color_1, fill_color_2, t->t(x,y));
|
||||
else if (point == SYMBOL_BORDER) return lerp(border_color_1, border_color_2, t->t(x,y));
|
||||
else return AColor(0,0,0,0);
|
||||
if (point == SYMBOL_INSIDE) return lerp(fill_color_1, fill_color_2, t->t(x,y));
|
||||
else if (point == SYMBOL_BORDER) return lerp(border_color_1, border_color_2, t->t(x,y));
|
||||
else return AColor(0,0,0,0);
|
||||
}
|
||||
|
||||
bool GradientSymbolFilter::equal(const GradientSymbolFilter& that) const {
|
||||
return fill_color_1 == that.fill_color_1
|
||||
&& fill_color_2 == that.fill_color_2
|
||||
&& border_color_1 == that.border_color_1
|
||||
&& border_color_2 == that.border_color_2;
|
||||
return fill_color_1 == that.fill_color_1
|
||||
&& fill_color_2 == that.fill_color_2
|
||||
&& border_color_1 == that.border_color_1
|
||||
&& border_color_2 == that.border_color_2;
|
||||
}
|
||||
|
||||
IMPLEMENT_REFLECTION(GradientSymbolFilter) {
|
||||
REFLECT_BASE(SymbolFilter);
|
||||
REFLECT(fill_color_1);
|
||||
REFLECT(fill_color_2);
|
||||
REFLECT(border_color_1);
|
||||
REFLECT(border_color_2);
|
||||
REFLECT_BASE(SymbolFilter);
|
||||
REFLECT(fill_color_1);
|
||||
REFLECT(fill_color_2);
|
||||
REFLECT(border_color_1);
|
||||
REFLECT(border_color_2);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : LinearGradientSymbolFilter
|
||||
@@ -137,40 +137,40 @@ inline double sqr(double x) { return x * x; }
|
||||
String LinearGradientSymbolFilter::fillType() const { return _("linear gradient"); }
|
||||
|
||||
LinearGradientSymbolFilter::LinearGradientSymbolFilter()
|
||||
: center_x(0.5), center_y(0.5)
|
||||
, end_x(1), end_y(1)
|
||||
: center_x(0.5), center_y(0.5)
|
||||
, end_x(1), end_y(1)
|
||||
{}
|
||||
LinearGradientSymbolFilter::LinearGradientSymbolFilter
|
||||
( const Color& fill_color_1, const Color& border_color_1
|
||||
, const Color& fill_color_2, const Color& border_color_2
|
||||
, double center_x, double center_y, double end_x, double end_y)
|
||||
: GradientSymbolFilter(fill_color_1, border_color_1, fill_color_2, border_color_2)
|
||||
, center_x(center_x), center_y(center_y)
|
||||
, end_x(end_x), end_y(end_y)
|
||||
( const Color& fill_color_1, const Color& border_color_1
|
||||
, const Color& fill_color_2, const Color& border_color_2
|
||||
, double center_x, double center_y, double end_x, double end_y)
|
||||
: GradientSymbolFilter(fill_color_1, border_color_1, fill_color_2, border_color_2)
|
||||
, center_x(center_x), center_y(center_y)
|
||||
, end_x(end_x), end_y(end_y)
|
||||
{}
|
||||
|
||||
AColor LinearGradientSymbolFilter::color(double x, double y, SymbolSet point) const {
|
||||
len = sqr(end_x - center_x) + sqr(end_y - center_y);
|
||||
if (len == 0) len = 1; // prevent div by 0
|
||||
return GradientSymbolFilter::color(x,y,point,this);
|
||||
len = sqr(end_x - center_x) + sqr(end_y - center_y);
|
||||
if (len == 0) len = 1; // prevent div by 0
|
||||
return GradientSymbolFilter::color(x,y,point,this);
|
||||
}
|
||||
|
||||
double LinearGradientSymbolFilter::t(double x, double y) const {
|
||||
double t= fabs( (x - center_x) * (end_x - center_x) + (y - center_y) * (end_y - center_y)) / len;
|
||||
return min(1.,max(0.,t));
|
||||
double t= fabs( (x - center_x) * (end_x - center_x) + (y - center_y) * (end_y - center_y)) / len;
|
||||
return min(1.,max(0.,t));
|
||||
}
|
||||
|
||||
bool LinearGradientSymbolFilter::operator == (const SymbolFilter& that) const {
|
||||
const LinearGradientSymbolFilter* that2 = dynamic_cast<const LinearGradientSymbolFilter*>(&that);
|
||||
return that2 && equal(*that2)
|
||||
&& center_x == that2->center_x && end_x == that2->end_x
|
||||
&& center_y == that2->center_y && end_y == that2->end_y;
|
||||
const LinearGradientSymbolFilter* that2 = dynamic_cast<const LinearGradientSymbolFilter*>(&that);
|
||||
return that2 && equal(*that2)
|
||||
&& center_x == that2->center_x && end_x == that2->end_x
|
||||
&& center_y == that2->center_y && end_y == that2->end_y;
|
||||
}
|
||||
|
||||
IMPLEMENT_REFLECTION(LinearGradientSymbolFilter) {
|
||||
REFLECT_BASE(GradientSymbolFilter);
|
||||
REFLECT(center_x); REFLECT(center_y);
|
||||
REFLECT(end_x); REFLECT(end_y);
|
||||
REFLECT_BASE(GradientSymbolFilter);
|
||||
REFLECT(center_x); REFLECT(center_y);
|
||||
REFLECT(end_x); REFLECT(end_y);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : RadialGradientSymbolFilter
|
||||
@@ -178,14 +178,14 @@ IMPLEMENT_REFLECTION(LinearGradientSymbolFilter) {
|
||||
String RadialGradientSymbolFilter::fillType() const { return _("radial gradient"); }
|
||||
|
||||
AColor RadialGradientSymbolFilter::color(double x, double y, SymbolSet point) const {
|
||||
return GradientSymbolFilter::color(x,y,point,this);
|
||||
return GradientSymbolFilter::color(x,y,point,this);
|
||||
}
|
||||
|
||||
double RadialGradientSymbolFilter::t(double x, double y) const {
|
||||
return sqrt( (sqr(x - 0.5) + sqr(y - 0.5)) * 2);
|
||||
return sqrt( (sqr(x - 0.5) + sqr(y - 0.5)) * 2);
|
||||
}
|
||||
|
||||
bool RadialGradientSymbolFilter::operator == (const SymbolFilter& that) const {
|
||||
const RadialGradientSymbolFilter* that2 = dynamic_cast<const RadialGradientSymbolFilter*>(&that);
|
||||
return that2 && equal(*that2);
|
||||
const RadialGradientSymbolFilter* that2 = dynamic_cast<const RadialGradientSymbolFilter*>(&that);
|
||||
return that2 && equal(*that2);
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ Image render_symbol(const SymbolP& symbol, const SymbolFilter& filter, double bo
|
||||
|
||||
/// Is a point inside a symbol?
|
||||
enum SymbolSet
|
||||
{ SYMBOL_INSIDE
|
||||
, SYMBOL_BORDER
|
||||
, SYMBOL_OUTSIDE
|
||||
{ SYMBOL_INSIDE
|
||||
, SYMBOL_BORDER
|
||||
, SYMBOL_OUTSIDE
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : SymbolFilter
|
||||
@@ -39,16 +39,16 @@ enum SymbolSet
|
||||
/// Base class for symbol filters
|
||||
class SymbolFilter : public IntrusivePtrVirtualBase {
|
||||
public:
|
||||
virtual ~SymbolFilter() {}
|
||||
/// What color should the symbol have at location (x, y)?
|
||||
/** x,y are in the range [0...1) */
|
||||
virtual AColor color(double x, double y, SymbolSet point) const = 0;
|
||||
/// Name of this fill type
|
||||
virtual String fillType() const = 0;
|
||||
/// Comparision
|
||||
virtual bool operator == (const SymbolFilter& that) const = 0;
|
||||
|
||||
DECLARE_REFLECTION_VIRTUAL();
|
||||
virtual ~SymbolFilter() {}
|
||||
/// What color should the symbol have at location (x, y)?
|
||||
/** x,y are in the range [0...1) */
|
||||
virtual AColor color(double x, double y, SymbolSet point) const = 0;
|
||||
/// Name of this fill type
|
||||
virtual String fillType() const = 0;
|
||||
/// Comparision
|
||||
virtual bool operator == (const SymbolFilter& that) const = 0;
|
||||
|
||||
DECLARE_REFLECTION_VIRTUAL();
|
||||
};
|
||||
|
||||
template <>
|
||||
@@ -59,71 +59,71 @@ intrusive_ptr<SymbolFilter> read_new<SymbolFilter>(Reader& reader);
|
||||
/// Symbol filter that returns solid colors
|
||||
class SolidFillSymbolFilter : public SymbolFilter {
|
||||
public:
|
||||
inline SolidFillSymbolFilter() {}
|
||||
inline SolidFillSymbolFilter(const AColor& fill_color, const AColor& border_color)
|
||||
: fill_color(fill_color), border_color(border_color)
|
||||
{}
|
||||
virtual AColor color(double x, double y, SymbolSet point) const;
|
||||
virtual String fillType() const;
|
||||
virtual bool operator == (const SymbolFilter& that) const;
|
||||
inline SolidFillSymbolFilter() {}
|
||||
inline SolidFillSymbolFilter(const AColor& fill_color, const AColor& border_color)
|
||||
: fill_color(fill_color), border_color(border_color)
|
||||
{}
|
||||
virtual AColor color(double x, double y, SymbolSet point) const;
|
||||
virtual String fillType() const;
|
||||
virtual bool operator == (const SymbolFilter& that) const;
|
||||
private:
|
||||
AColor fill_color, border_color;
|
||||
DECLARE_REFLECTION();
|
||||
AColor fill_color, border_color;
|
||||
DECLARE_REFLECTION();
|
||||
};
|
||||
|
||||
/// Symbol filter that returns some gradient
|
||||
class GradientSymbolFilter : public SymbolFilter {
|
||||
public:
|
||||
inline GradientSymbolFilter() {}
|
||||
inline GradientSymbolFilter(const Color& fill_color_1, const Color& border_color_1, const Color& fill_color_2, const Color& border_color_2)
|
||||
: fill_color_1(fill_color_1), border_color_1(border_color_1)
|
||||
, fill_color_2(fill_color_2), border_color_2(border_color_2)
|
||||
{}
|
||||
inline GradientSymbolFilter() {}
|
||||
inline GradientSymbolFilter(const Color& fill_color_1, const Color& border_color_1, const Color& fill_color_2, const Color& border_color_2)
|
||||
: fill_color_1(fill_color_1), border_color_1(border_color_1)
|
||||
, fill_color_2(fill_color_2), border_color_2(border_color_2)
|
||||
{}
|
||||
protected:
|
||||
Color fill_color_1, border_color_1;
|
||||
Color fill_color_2, border_color_2;
|
||||
template <typename T>
|
||||
AColor color(double x, double y, SymbolSet point, const T* t) const;
|
||||
bool equal(const GradientSymbolFilter& that) const;
|
||||
|
||||
DECLARE_REFLECTION();
|
||||
Color fill_color_1, border_color_1;
|
||||
Color fill_color_2, border_color_2;
|
||||
template <typename T>
|
||||
AColor color(double x, double y, SymbolSet point, const T* t) const;
|
||||
bool equal(const GradientSymbolFilter& that) const;
|
||||
|
||||
DECLARE_REFLECTION();
|
||||
};
|
||||
|
||||
/// Symbol filter that returns a linear gradient
|
||||
class LinearGradientSymbolFilter : public GradientSymbolFilter {
|
||||
public:
|
||||
LinearGradientSymbolFilter();
|
||||
LinearGradientSymbolFilter(const Color& fill_color_1, const Color& border_color_1, const Color& fill_color_2, const Color& border_color_2
|
||||
,double center_x, double center_y, double end_x, double end_y);
|
||||
|
||||
virtual AColor color(double x, double y, SymbolSet point) const;
|
||||
virtual String fillType() const;
|
||||
virtual bool operator == (const SymbolFilter& that) const;
|
||||
|
||||
/// return time on the gradient, used by GradientSymbolFilter::color
|
||||
inline double t(double x, double y) const;
|
||||
|
||||
LinearGradientSymbolFilter();
|
||||
LinearGradientSymbolFilter(const Color& fill_color_1, const Color& border_color_1, const Color& fill_color_2, const Color& border_color_2
|
||||
,double center_x, double center_y, double end_x, double end_y);
|
||||
|
||||
virtual AColor color(double x, double y, SymbolSet point) const;
|
||||
virtual String fillType() const;
|
||||
virtual bool operator == (const SymbolFilter& that) const;
|
||||
|
||||
/// return time on the gradient, used by GradientSymbolFilter::color
|
||||
inline double t(double x, double y) const;
|
||||
|
||||
private:
|
||||
double center_x, center_y;
|
||||
double end_x, end_y;
|
||||
mutable double len;
|
||||
DECLARE_REFLECTION();
|
||||
double center_x, center_y;
|
||||
double end_x, end_y;
|
||||
mutable double len;
|
||||
DECLARE_REFLECTION();
|
||||
};
|
||||
|
||||
/// Symbol filter that returns a radial gradient
|
||||
class RadialGradientSymbolFilter : public GradientSymbolFilter {
|
||||
public:
|
||||
inline RadialGradientSymbolFilter() {}
|
||||
inline RadialGradientSymbolFilter(const Color& fill_color_1, const Color& border_color_1, const Color& fill_color_2, const Color& border_color_2)
|
||||
: GradientSymbolFilter(fill_color_1, border_color_1, fill_color_2, border_color_2)
|
||||
{}
|
||||
|
||||
virtual AColor color(double x, double y, SymbolSet point) const;
|
||||
virtual String fillType() const;
|
||||
virtual bool operator == (const SymbolFilter& that) const;
|
||||
|
||||
/// return time on the gradient, used by GradientSymbolFilter::color
|
||||
inline double t(double x, double y) const;
|
||||
inline RadialGradientSymbolFilter() {}
|
||||
inline RadialGradientSymbolFilter(const Color& fill_color_1, const Color& border_color_1, const Color& fill_color_2, const Color& border_color_2)
|
||||
: GradientSymbolFilter(fill_color_1, border_color_1, fill_color_2, border_color_2)
|
||||
{}
|
||||
|
||||
virtual AColor color(double x, double y, SymbolSet point) const;
|
||||
virtual String fillType() const;
|
||||
virtual bool operator == (const SymbolFilter& that) const;
|
||||
|
||||
/// return time on the gradient, used by GradientSymbolFilter::color
|
||||
inline double t(double x, double y) const;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+299
-299
@@ -16,53 +16,53 @@ DECLARE_TYPEOF_COLLECTION(SymbolPartP);
|
||||
// ----------------------------------------------------------------------------- : Simple rendering
|
||||
|
||||
Image render_symbol(const SymbolP& symbol, double border_radius, int width, int height, bool editing_hints, bool allow_smaller) {
|
||||
SymbolViewer viewer(symbol, editing_hints, width, border_radius);
|
||||
// limit width/height ratio to aspect ratio of symbol
|
||||
double ar = symbol->aspectRatio();
|
||||
double par = (double)width/height;
|
||||
if (par > ar && (ar > 1 || (allow_smaller && height < width))) {
|
||||
width = int(height * ar);
|
||||
} else if (par < ar && (ar < 1 || (allow_smaller && width < height))) {
|
||||
height = int(width / ar);
|
||||
}
|
||||
if (width > height) {
|
||||
viewer.setZoom(width);
|
||||
viewer.setOrigin(Vector2D(0,-(width-height) * 0.5));
|
||||
viewer.border_radius *= (double)height / width;
|
||||
} else {
|
||||
viewer.setZoom(height);
|
||||
viewer.setOrigin(Vector2D(-(height-width) * 0.5,0));
|
||||
viewer.border_radius *= (double)width / height;
|
||||
}
|
||||
Bitmap bmp(width, height);
|
||||
wxMemoryDC dc;
|
||||
dc.SelectObject(bmp);
|
||||
clearDC(dc, Color(0,128,0));
|
||||
viewer.draw(dc);
|
||||
dc.SelectObject(wxNullBitmap);
|
||||
return bmp.ConvertToImage();
|
||||
SymbolViewer viewer(symbol, editing_hints, width, border_radius);
|
||||
// limit width/height ratio to aspect ratio of symbol
|
||||
double ar = symbol->aspectRatio();
|
||||
double par = (double)width/height;
|
||||
if (par > ar && (ar > 1 || (allow_smaller && height < width))) {
|
||||
width = int(height * ar);
|
||||
} else if (par < ar && (ar < 1 || (allow_smaller && width < height))) {
|
||||
height = int(width / ar);
|
||||
}
|
||||
if (width > height) {
|
||||
viewer.setZoom(width);
|
||||
viewer.setOrigin(Vector2D(0,-(width-height) * 0.5));
|
||||
viewer.border_radius *= (double)height / width;
|
||||
} else {
|
||||
viewer.setZoom(height);
|
||||
viewer.setOrigin(Vector2D(-(height-width) * 0.5,0));
|
||||
viewer.border_radius *= (double)width / height;
|
||||
}
|
||||
Bitmap bmp(width, height);
|
||||
wxMemoryDC dc;
|
||||
dc.SelectObject(bmp);
|
||||
clearDC(dc, Color(0,128,0));
|
||||
viewer.draw(dc);
|
||||
dc.SelectObject(wxNullBitmap);
|
||||
return bmp.ConvertToImage();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Constructor
|
||||
|
||||
SymbolViewer::SymbolViewer(const SymbolP& symbol, bool editing_hints, double size, double border_radius)
|
||||
: border_radius(border_radius), editing_hints(editing_hints)
|
||||
, rotation(0, RealRect(0,0,size,size), size)
|
||||
, multiply(size,0,0,size)
|
||||
, origin(0,0)
|
||||
, in_symmetry(0)
|
||||
: border_radius(border_radius), editing_hints(editing_hints)
|
||||
, rotation(0, RealRect(0,0,size,size), size)
|
||||
, multiply(size,0,0,size)
|
||||
, origin(0,0)
|
||||
, in_symmetry(0)
|
||||
{
|
||||
setSymbol(symbol);
|
||||
setSymbol(symbol);
|
||||
}
|
||||
|
||||
void SymbolViewer::setZoom(double zoom) {
|
||||
rotation.setZoom(zoom);
|
||||
rotation.setOrigin(zoom * origin);
|
||||
multiply = Matrix2D(zoom,0, 0,zoom);
|
||||
rotation.setZoom(zoom);
|
||||
rotation.setOrigin(zoom * origin);
|
||||
multiply = Matrix2D(zoom,0, 0,zoom);
|
||||
}
|
||||
void SymbolViewer::setOrigin(const Vector2D& origin) {
|
||||
this->origin = origin;
|
||||
rotation.setOrigin(origin);
|
||||
this->origin = origin;
|
||||
rotation.setOrigin(origin);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Drawing : Combining
|
||||
@@ -71,185 +71,185 @@ typedef shared_ptr<wxMemoryDC> MemoryDCP;
|
||||
|
||||
// Return a temporary DC with the same size as the parameter
|
||||
MemoryDCP getTempDC(DC& dc) {
|
||||
wxSize s = dc.GetSize();
|
||||
#ifdef __WXMSW__
|
||||
Bitmap buffer(s.GetWidth(), s.GetHeight(), 1);
|
||||
#else
|
||||
Bitmap buffer(s.GetWidth(), s.GetHeight(), 24);
|
||||
#endif
|
||||
MemoryDCP newDC(new wxMemoryDC);
|
||||
newDC->SelectObject(buffer);
|
||||
clearDC(*newDC, *wxBLACK_BRUSH);
|
||||
return newDC;
|
||||
wxSize s = dc.GetSize();
|
||||
#ifdef __WXMSW__
|
||||
Bitmap buffer(s.GetWidth(), s.GetHeight(), 1);
|
||||
#else
|
||||
Bitmap buffer(s.GetWidth(), s.GetHeight(), 24);
|
||||
#endif
|
||||
MemoryDCP newDC(new wxMemoryDC);
|
||||
newDC->SelectObject(buffer);
|
||||
clearDC(*newDC, *wxBLACK_BRUSH);
|
||||
return newDC;
|
||||
}
|
||||
|
||||
// Combine the temporary DCs used in the drawing with the main dc
|
||||
void combineBuffers(DC& dc, DC* borders, DC* interior) {
|
||||
wxSize s = dc.GetSize();
|
||||
if (borders) dc.Blit(0, 0, s.GetWidth(), s.GetHeight(), borders, 0, 0, wxOR);
|
||||
if (interior) dc.Blit(0, 0, s.GetWidth(), s.GetHeight(), interior, 0, 0, wxAND_INVERT);
|
||||
wxSize s = dc.GetSize();
|
||||
if (borders) dc.Blit(0, 0, s.GetWidth(), s.GetHeight(), borders, 0, 0, wxOR);
|
||||
if (interior) dc.Blit(0, 0, s.GetWidth(), s.GetHeight(), interior, 0, 0, wxAND_INVERT);
|
||||
}
|
||||
|
||||
void SymbolViewer::draw(DC& dc) {
|
||||
bool paintedSomething = false;
|
||||
bool buffersFilled = false;
|
||||
in_symmetry = 0;
|
||||
// Temporary dcs
|
||||
MemoryDCP borderDC;
|
||||
MemoryDCP interiorDC;
|
||||
// Check if we can paint directly to the dc
|
||||
// This will fail if there are parts with combine == intersection
|
||||
FOR_EACH(p, symbol->parts) {
|
||||
if (SymbolShape* s = p->isSymbolShape()) {
|
||||
if (s->combine == SYMBOL_COMBINE_INTERSECTION) {
|
||||
paintedSomething = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Draw all parts
|
||||
combineSymbolPart(dc, *symbol, paintedSomething, buffersFilled, true, borderDC, interiorDC);
|
||||
// Output the final parts from the buffer
|
||||
if (buffersFilled) {
|
||||
combineBuffers(dc, borderDC.get(), interiorDC.get());
|
||||
}
|
||||
// Editing hints?
|
||||
if (editing_hints) {
|
||||
drawEditingHints(dc);
|
||||
}
|
||||
bool paintedSomething = false;
|
||||
bool buffersFilled = false;
|
||||
in_symmetry = 0;
|
||||
// Temporary dcs
|
||||
MemoryDCP borderDC;
|
||||
MemoryDCP interiorDC;
|
||||
// Check if we can paint directly to the dc
|
||||
// This will fail if there are parts with combine == intersection
|
||||
FOR_EACH(p, symbol->parts) {
|
||||
if (SymbolShape* s = p->isSymbolShape()) {
|
||||
if (s->combine == SYMBOL_COMBINE_INTERSECTION) {
|
||||
paintedSomething = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Draw all parts
|
||||
combineSymbolPart(dc, *symbol, paintedSomething, buffersFilled, true, borderDC, interiorDC);
|
||||
// Output the final parts from the buffer
|
||||
if (buffersFilled) {
|
||||
combineBuffers(dc, borderDC.get(), interiorDC.get());
|
||||
}
|
||||
// Editing hints?
|
||||
if (editing_hints) {
|
||||
drawEditingHints(dc);
|
||||
}
|
||||
}
|
||||
void SymbolViewer::combineSymbolPart(DC& dc, const SymbolPart& part, bool& paintedSomething, bool& buffersFilled, bool allow_overlap, MemoryDCP& borderDC, MemoryDCP& interiorDC) {
|
||||
if (const SymbolShape* s = part.isSymbolShape()) {
|
||||
if (s->combine == SYMBOL_COMBINE_OVERLAP && buffersFilled && allow_overlap) {
|
||||
// We will be overlapping some previous parts, write them to the screen
|
||||
combineBuffers(dc, borderDC.get(), interiorDC.get());
|
||||
// Clear the buffers
|
||||
buffersFilled = false;
|
||||
paintedSomething = true;
|
||||
wxSize s = dc.GetSize();
|
||||
if (borderDC) {
|
||||
borderDC->SetBrush(*wxBLACK_BRUSH);
|
||||
borderDC->SetPen( *wxTRANSPARENT_PEN);
|
||||
borderDC->DrawRectangle(0, 0, s.GetWidth(), s.GetHeight());
|
||||
}
|
||||
interiorDC->SetBrush(*wxBLACK_BRUSH);
|
||||
interiorDC->DrawRectangle(0, 0, s.GetWidth(), s.GetHeight());
|
||||
}
|
||||
|
||||
// Paint the part itself
|
||||
if (!paintedSomething) {
|
||||
// No need to buffer
|
||||
if (!interiorDC) interiorDC = getTempDC(dc);
|
||||
combineSymbolShape(*s, dc, *interiorDC, true, false);
|
||||
buffersFilled = true;
|
||||
} else {
|
||||
if (!borderDC) borderDC = getTempDC(dc);
|
||||
if (!interiorDC) interiorDC = getTempDC(dc);
|
||||
// Draw this shape to the buffer
|
||||
combineSymbolShape(*s, *borderDC, *interiorDC, false, false);
|
||||
buffersFilled = true;
|
||||
}
|
||||
} else if (const SymbolSymmetry* s = part.isSymbolSymmetry()) {
|
||||
// Draw all parts, in reverse order (bottom to top), also draw rotated copies
|
||||
Radians b = 2 * s->handle.angle();
|
||||
Matrix2D old_m = multiply;
|
||||
Vector2D old_o = origin;
|
||||
int copies = s->kind == SYMMETRY_REFLECTION ? s->copies / 2 * 2 : s->copies;
|
||||
FOR_EACH_CONST_REVERSE(p, s->parts) {
|
||||
if (copies > 1) ++in_symmetry;
|
||||
for (int i = copies - 1 ; i >= 0 ; --i) {
|
||||
if (i == 0) --in_symmetry;
|
||||
if (s->clip) {
|
||||
// todo: clip
|
||||
}
|
||||
double a = i * 2 * M_PI / copies;
|
||||
if (s->kind == SYMMETRY_ROTATION || i % 2 == 0) {
|
||||
// set matrix
|
||||
// Calling:
|
||||
// - p the input point
|
||||
// - p' the output point
|
||||
// - rot our rotation matrix
|
||||
// - d out origin
|
||||
// - o the current origin (old_o)
|
||||
// - m the current matrix (old_m)
|
||||
// We want:
|
||||
// p' = ((p - d) * rot + d) * m + o
|
||||
// = (p * rot - d * rot + d) * m + o
|
||||
// = p * rot * m + (d - d * rot) * m + o
|
||||
Matrix2D rot(cos(a),-sin(a), sin(a),cos(a));
|
||||
multiply = rot * old_m;
|
||||
origin = old_o + (s->center - s->center * rot) * old_m;
|
||||
} else {
|
||||
// reflection
|
||||
// Calling angle = b
|
||||
// Matrix2D ref(cos(b),sin(b), sin(b),-cos(b));
|
||||
// Matrix2D rot(cos(a),-sin(a), sin(a),cos(a));
|
||||
//
|
||||
// ref * rot
|
||||
// [ cos b sin b ! [ cos a -sin a !
|
||||
// = ! sin b -cos b ] ! sin a cos a ]
|
||||
// = [ cos(a+b) sin(a+b) !
|
||||
// ! sin(a+b) -cos(a+b) ]
|
||||
Matrix2D rot(cos(a+b),sin(a+b), sin(a+b),-cos(a+b));
|
||||
multiply = rot * old_m;
|
||||
origin = old_o + (s->center - s->center * rot) * old_m;
|
||||
}
|
||||
// draw rotated copy
|
||||
combineSymbolPart(dc, *p, paintedSomething, buffersFilled, allow_overlap && i == copies - 1, borderDC, interiorDC);
|
||||
}
|
||||
}
|
||||
multiply = old_m;
|
||||
origin = old_o;
|
||||
if (editing_hints) {
|
||||
highlightPart(dc, *s, HIGHLIGHT_LESS);
|
||||
}
|
||||
} else if (const SymbolGroup* g = part.isSymbolGroup()) {
|
||||
// Draw all parts, in reverse order (bottom to top)
|
||||
FOR_EACH_CONST_REVERSE(p, g->parts) {
|
||||
combineSymbolPart(dc, *p, paintedSomething, buffersFilled, allow_overlap, borderDC, interiorDC);
|
||||
}
|
||||
}
|
||||
if (const SymbolShape* s = part.isSymbolShape()) {
|
||||
if (s->combine == SYMBOL_COMBINE_OVERLAP && buffersFilled && allow_overlap) {
|
||||
// We will be overlapping some previous parts, write them to the screen
|
||||
combineBuffers(dc, borderDC.get(), interiorDC.get());
|
||||
// Clear the buffers
|
||||
buffersFilled = false;
|
||||
paintedSomething = true;
|
||||
wxSize s = dc.GetSize();
|
||||
if (borderDC) {
|
||||
borderDC->SetBrush(*wxBLACK_BRUSH);
|
||||
borderDC->SetPen( *wxTRANSPARENT_PEN);
|
||||
borderDC->DrawRectangle(0, 0, s.GetWidth(), s.GetHeight());
|
||||
}
|
||||
interiorDC->SetBrush(*wxBLACK_BRUSH);
|
||||
interiorDC->DrawRectangle(0, 0, s.GetWidth(), s.GetHeight());
|
||||
}
|
||||
|
||||
// Paint the part itself
|
||||
if (!paintedSomething) {
|
||||
// No need to buffer
|
||||
if (!interiorDC) interiorDC = getTempDC(dc);
|
||||
combineSymbolShape(*s, dc, *interiorDC, true, false);
|
||||
buffersFilled = true;
|
||||
} else {
|
||||
if (!borderDC) borderDC = getTempDC(dc);
|
||||
if (!interiorDC) interiorDC = getTempDC(dc);
|
||||
// Draw this shape to the buffer
|
||||
combineSymbolShape(*s, *borderDC, *interiorDC, false, false);
|
||||
buffersFilled = true;
|
||||
}
|
||||
} else if (const SymbolSymmetry* s = part.isSymbolSymmetry()) {
|
||||
// Draw all parts, in reverse order (bottom to top), also draw rotated copies
|
||||
Radians b = 2 * s->handle.angle();
|
||||
Matrix2D old_m = multiply;
|
||||
Vector2D old_o = origin;
|
||||
int copies = s->kind == SYMMETRY_REFLECTION ? s->copies / 2 * 2 : s->copies;
|
||||
FOR_EACH_CONST_REVERSE(p, s->parts) {
|
||||
if (copies > 1) ++in_symmetry;
|
||||
for (int i = copies - 1 ; i >= 0 ; --i) {
|
||||
if (i == 0) --in_symmetry;
|
||||
if (s->clip) {
|
||||
// todo: clip
|
||||
}
|
||||
double a = i * 2 * M_PI / copies;
|
||||
if (s->kind == SYMMETRY_ROTATION || i % 2 == 0) {
|
||||
// set matrix
|
||||
// Calling:
|
||||
// - p the input point
|
||||
// - p' the output point
|
||||
// - rot our rotation matrix
|
||||
// - d out origin
|
||||
// - o the current origin (old_o)
|
||||
// - m the current matrix (old_m)
|
||||
// We want:
|
||||
// p' = ((p - d) * rot + d) * m + o
|
||||
// = (p * rot - d * rot + d) * m + o
|
||||
// = p * rot * m + (d - d * rot) * m + o
|
||||
Matrix2D rot(cos(a),-sin(a), sin(a),cos(a));
|
||||
multiply = rot * old_m;
|
||||
origin = old_o + (s->center - s->center * rot) * old_m;
|
||||
} else {
|
||||
// reflection
|
||||
// Calling angle = b
|
||||
// Matrix2D ref(cos(b),sin(b), sin(b),-cos(b));
|
||||
// Matrix2D rot(cos(a),-sin(a), sin(a),cos(a));
|
||||
//
|
||||
// ref * rot
|
||||
// [ cos b sin b ! [ cos a -sin a !
|
||||
// = ! sin b -cos b ] ! sin a cos a ]
|
||||
// = [ cos(a+b) sin(a+b) !
|
||||
// ! sin(a+b) -cos(a+b) ]
|
||||
Matrix2D rot(cos(a+b),sin(a+b), sin(a+b),-cos(a+b));
|
||||
multiply = rot * old_m;
|
||||
origin = old_o + (s->center - s->center * rot) * old_m;
|
||||
}
|
||||
// draw rotated copy
|
||||
combineSymbolPart(dc, *p, paintedSomething, buffersFilled, allow_overlap && i == copies - 1, borderDC, interiorDC);
|
||||
}
|
||||
}
|
||||
multiply = old_m;
|
||||
origin = old_o;
|
||||
if (editing_hints) {
|
||||
highlightPart(dc, *s, HIGHLIGHT_LESS);
|
||||
}
|
||||
} else if (const SymbolGroup* g = part.isSymbolGroup()) {
|
||||
// Draw all parts, in reverse order (bottom to top)
|
||||
FOR_EACH_CONST_REVERSE(p, g->parts) {
|
||||
combineSymbolPart(dc, *p, paintedSomething, buffersFilled, allow_overlap, borderDC, interiorDC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SymbolViewer::combineSymbolShape(const SymbolShape& shape, DC& border, DC& interior, bool directB, bool directI) {
|
||||
// what color should the interior be?
|
||||
// use black when drawing to the screen
|
||||
Byte interiorCol = directI ? 0 : 255;
|
||||
if (editing_hints && in_symmetry) {
|
||||
interiorCol = directI ? 16 : 240;
|
||||
}
|
||||
// how to draw depends on combining mode
|
||||
switch(shape.combine) {
|
||||
case SYMBOL_COMBINE_OVERLAP:
|
||||
case SYMBOL_COMBINE_MERGE: {
|
||||
drawSymbolShape(shape, &border, &interior, 255, interiorCol, directB, false);
|
||||
break;
|
||||
} case SYMBOL_COMBINE_SUBTRACT: {
|
||||
border.SetLogicalFunction(wxAND);
|
||||
drawSymbolShape(shape, &border, &interior, 0, ~interiorCol, directB, false);
|
||||
border.SetLogicalFunction(wxCOPY);
|
||||
break;
|
||||
} case SYMBOL_COMBINE_INTERSECTION: {
|
||||
MemoryDCP keepBorder = getTempDC(border);
|
||||
MemoryDCP keepInterior = getTempDC(interior);
|
||||
drawSymbolShape(shape, keepBorder.get(), keepInterior.get(), 255, 255, false, false);
|
||||
// combine the temporary dcs with the result using the AND operator
|
||||
wxSize s = border.GetSize();
|
||||
border .Blit(0, 0, s.GetWidth(), s.GetHeight(), &*keepBorder , 0, 0, wxAND);
|
||||
interior.Blit(0, 0, s.GetWidth(), s.GetHeight(), &*keepInterior, 0, 0, wxAND);
|
||||
break;
|
||||
} case SYMBOL_COMBINE_DIFFERENCE: {
|
||||
interior.SetLogicalFunction(wxXOR);
|
||||
drawSymbolShape(shape, &border, &interior, 0, interiorCol, directB, true);
|
||||
interior.SetLogicalFunction(wxCOPY);
|
||||
break;
|
||||
} case SYMBOL_COMBINE_BORDER: {
|
||||
// draw border as interior
|
||||
drawSymbolShape(shape, nullptr, &border, 0, 255, false, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// what color should the interior be?
|
||||
// use black when drawing to the screen
|
||||
Byte interiorCol = directI ? 0 : 255;
|
||||
if (editing_hints && in_symmetry) {
|
||||
interiorCol = directI ? 16 : 240;
|
||||
}
|
||||
// how to draw depends on combining mode
|
||||
switch(shape.combine) {
|
||||
case SYMBOL_COMBINE_OVERLAP:
|
||||
case SYMBOL_COMBINE_MERGE: {
|
||||
drawSymbolShape(shape, &border, &interior, 255, interiorCol, directB, false);
|
||||
break;
|
||||
} case SYMBOL_COMBINE_SUBTRACT: {
|
||||
border.SetLogicalFunction(wxAND);
|
||||
drawSymbolShape(shape, &border, &interior, 0, ~interiorCol, directB, false);
|
||||
border.SetLogicalFunction(wxCOPY);
|
||||
break;
|
||||
} case SYMBOL_COMBINE_INTERSECTION: {
|
||||
MemoryDCP keepBorder = getTempDC(border);
|
||||
MemoryDCP keepInterior = getTempDC(interior);
|
||||
drawSymbolShape(shape, keepBorder.get(), keepInterior.get(), 255, 255, false, false);
|
||||
// combine the temporary dcs with the result using the AND operator
|
||||
wxSize s = border.GetSize();
|
||||
border .Blit(0, 0, s.GetWidth(), s.GetHeight(), &*keepBorder , 0, 0, wxAND);
|
||||
interior.Blit(0, 0, s.GetWidth(), s.GetHeight(), &*keepInterior, 0, 0, wxAND);
|
||||
break;
|
||||
} case SYMBOL_COMBINE_DIFFERENCE: {
|
||||
interior.SetLogicalFunction(wxXOR);
|
||||
drawSymbolShape(shape, &border, &interior, 0, interiorCol, directB, true);
|
||||
interior.SetLogicalFunction(wxCOPY);
|
||||
break;
|
||||
} case SYMBOL_COMBINE_BORDER: {
|
||||
// draw border as interior
|
||||
drawSymbolShape(shape, nullptr, &border, 0, 255, false, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -257,121 +257,121 @@ void SymbolViewer::combineSymbolShape(const SymbolShape& shape, DC& border, DC&
|
||||
|
||||
|
||||
void SymbolViewer::drawSymbolShape(const SymbolShape& shape, DC* border, DC* interior, Byte borderCol, Byte interiorCol, bool directB, bool clear) {
|
||||
// create point list
|
||||
vector<wxPoint> points;
|
||||
size_t size = shape.points.size();
|
||||
for(size_t i = 0 ; i < size ; ++i) {
|
||||
segment_subdivide(*shape.getPoint((int)i), *shape.getPoint((int)i+1), origin, multiply, points);
|
||||
}
|
||||
// draw border
|
||||
if (border && border_radius > 0) {
|
||||
// white/black or, if directB white/green
|
||||
border->SetBrush(Color(borderCol, (directB && borderCol == 0 ? 128 : borderCol), borderCol));
|
||||
border->SetPen(wxPen(*wxWHITE, (int) rotation.trS(border_radius)));
|
||||
border->DrawPolygon((int)points.size(), &points[0]);
|
||||
// create point list
|
||||
vector<wxPoint> points;
|
||||
size_t size = shape.points.size();
|
||||
for(size_t i = 0 ; i < size ; ++i) {
|
||||
segment_subdivide(*shape.getPoint((int)i), *shape.getPoint((int)i+1), origin, multiply, points);
|
||||
}
|
||||
// draw border
|
||||
if (border && border_radius > 0) {
|
||||
// white/black or, if directB white/green
|
||||
border->SetBrush(Color(borderCol, (directB && borderCol == 0 ? 128 : borderCol), borderCol));
|
||||
border->SetPen(wxPen(*wxWHITE, (int) rotation.trS(border_radius)));
|
||||
border->DrawPolygon((int)points.size(), &points[0]);
|
||||
|
||||
if (clear) {
|
||||
border->SetPen(*wxTRANSPARENT_PEN);
|
||||
border->SetBrush(Color(0, (directB ? 128 : 0), 0));
|
||||
if (clear) {
|
||||
border->SetPen(*wxTRANSPARENT_PEN);
|
||||
border->SetBrush(Color(0, (directB ? 128 : 0), 0));
|
||||
|
||||
int func = border->GetLogicalFunction();
|
||||
border->SetLogicalFunction(wxCOPY);
|
||||
border->DrawPolygon((int)points.size(), &points[0]);
|
||||
border->SetLogicalFunction(func);
|
||||
}
|
||||
}
|
||||
// draw interior
|
||||
if (interior) {
|
||||
interior->SetBrush(Color(interiorCol,interiorCol,interiorCol));
|
||||
interior->SetPen(*wxTRANSPARENT_PEN);
|
||||
interior->DrawPolygon((int)points.size(), &points[0]);
|
||||
}
|
||||
int func = border->GetLogicalFunction();
|
||||
border->SetLogicalFunction(wxCOPY);
|
||||
border->DrawPolygon((int)points.size(), &points[0]);
|
||||
border->SetLogicalFunction(func);
|
||||
}
|
||||
}
|
||||
// draw interior
|
||||
if (interior) {
|
||||
interior->SetBrush(Color(interiorCol,interiorCol,interiorCol));
|
||||
interior->SetPen(*wxTRANSPARENT_PEN);
|
||||
interior->DrawPolygon((int)points.size(), &points[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Drawing : Highlighting
|
||||
|
||||
void SymbolViewer::highlightPart(DC& dc, const SymbolPart& part, HighlightStyle style) {
|
||||
if (const SymbolShape* s = part.isSymbolShape()) {
|
||||
highlightPart(dc, *s, style);
|
||||
} else if (const SymbolSymmetry* s = part.isSymbolSymmetry()) {
|
||||
highlightPart(dc, *s, style);
|
||||
} else if (const SymbolGroup* g = part.isSymbolGroup()) {
|
||||
highlightPart(dc, *g, style);
|
||||
} else {
|
||||
throw InternalError(_("Invalid symbol part type"));
|
||||
}
|
||||
if (const SymbolShape* s = part.isSymbolShape()) {
|
||||
highlightPart(dc, *s, style);
|
||||
} else if (const SymbolSymmetry* s = part.isSymbolSymmetry()) {
|
||||
highlightPart(dc, *s, style);
|
||||
} else if (const SymbolGroup* g = part.isSymbolGroup()) {
|
||||
highlightPart(dc, *g, style);
|
||||
} else {
|
||||
throw InternalError(_("Invalid symbol part type"));
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolViewer::highlightPart(DC& dc, const SymbolShape& shape, HighlightStyle style) {
|
||||
if (style == HIGHLIGHT_LESS) return;
|
||||
// create point list
|
||||
vector<wxPoint> points;
|
||||
size_t size = shape.points.size();
|
||||
for(size_t i = 0 ; i < size ; ++i) {
|
||||
segment_subdivide(*shape.getPoint((int)i), *shape.getPoint((int)i+1), origin, multiply, points);
|
||||
}
|
||||
// draw
|
||||
if (style == HIGHLIGHT_BORDER) {
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.SetPen (wxPen(Color(255,0,0), 2));
|
||||
dc.DrawPolygon((int)points.size(), &points[0]);
|
||||
} else if (style == HIGHLIGHT_BORDER_DOT) {
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.SetPen (wxPen(Color(255,0,0), 1, wxDOT));
|
||||
dc.DrawPolygon((int)points.size(), &points[0]);
|
||||
} else {
|
||||
dc.SetLogicalFunction(wxOR);
|
||||
dc.SetBrush(Color(0,0,64));
|
||||
dc.SetPen (*wxTRANSPARENT_PEN);
|
||||
dc.DrawPolygon((int)points.size(), &points[0]);
|
||||
if (shape.combine == SYMBOL_COMBINE_SUBTRACT || shape.combine == SYMBOL_COMBINE_BORDER) {
|
||||
dc.SetLogicalFunction(wxAND);
|
||||
dc.SetBrush(Color(191,191,255));
|
||||
dc.DrawPolygon((int)points.size(), &points[0]);
|
||||
}
|
||||
dc.SetLogicalFunction(wxCOPY);
|
||||
}
|
||||
if (style == HIGHLIGHT_LESS) return;
|
||||
// create point list
|
||||
vector<wxPoint> points;
|
||||
size_t size = shape.points.size();
|
||||
for(size_t i = 0 ; i < size ; ++i) {
|
||||
segment_subdivide(*shape.getPoint((int)i), *shape.getPoint((int)i+1), origin, multiply, points);
|
||||
}
|
||||
// draw
|
||||
if (style == HIGHLIGHT_BORDER) {
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.SetPen (wxPen(Color(255,0,0), 2));
|
||||
dc.DrawPolygon((int)points.size(), &points[0]);
|
||||
} else if (style == HIGHLIGHT_BORDER_DOT) {
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.SetPen (wxPen(Color(255,0,0), 1, wxDOT));
|
||||
dc.DrawPolygon((int)points.size(), &points[0]);
|
||||
} else {
|
||||
dc.SetLogicalFunction(wxOR);
|
||||
dc.SetBrush(Color(0,0,64));
|
||||
dc.SetPen (*wxTRANSPARENT_PEN);
|
||||
dc.DrawPolygon((int)points.size(), &points[0]);
|
||||
if (shape.combine == SYMBOL_COMBINE_SUBTRACT || shape.combine == SYMBOL_COMBINE_BORDER) {
|
||||
dc.SetLogicalFunction(wxAND);
|
||||
dc.SetBrush(Color(191,191,255));
|
||||
dc.DrawPolygon((int)points.size(), &points[0]);
|
||||
}
|
||||
dc.SetLogicalFunction(wxCOPY);
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolViewer::highlightPart(DC& dc, const SymbolSymmetry& sym, HighlightStyle style) {
|
||||
// highlight parts?
|
||||
FOR_EACH_CONST(part, sym.parts) {
|
||||
highlightPart(dc, *part, (HighlightStyle)(style | HIGHLIGHT_LESS));
|
||||
}
|
||||
// Color?
|
||||
Color color = style & HIGHLIGHT_BORDER ? Color(255,100,0)
|
||||
: style & HIGHLIGHT_INTERIOR ? Color(255,200,0)
|
||||
: Color(200,170,0);
|
||||
// center
|
||||
RealPoint center = rotation.tr(sym.center);
|
||||
// draw 'spokes'
|
||||
Radians angle = atan2(sym.handle.y, sym.handle.x);
|
||||
dc.SetPen(wxPen(color, sym.kind == SYMMETRY_ROTATION ? 1 : 3));
|
||||
int copies = sym.kind == SYMMETRY_REFLECTION ? sym.copies / 2 * 2 : sym.copies;
|
||||
for (int i = 0; i < copies ; ++i) {
|
||||
Radians a = angle + (i + 0.5) * 2 * M_PI / copies;
|
||||
Vector2D dir(cos(a), sin(a));
|
||||
Vector2D dir2 = rotation.tr(sym.center + 2 * dir);
|
||||
dc.DrawLine(int(center.x), int(center.y), int(dir2.x), int(dir2.y));
|
||||
}
|
||||
// draw center
|
||||
dc.SetPen(*wxBLACK_PEN);
|
||||
dc.SetBrush(color);
|
||||
dc.DrawCircle(int(center.x), int(center.y), sym.kind == SYMMETRY_ROTATION ? 7 : 5);
|
||||
// highlight parts?
|
||||
FOR_EACH_CONST(part, sym.parts) {
|
||||
highlightPart(dc, *part, (HighlightStyle)(style | HIGHLIGHT_LESS));
|
||||
}
|
||||
// Color?
|
||||
Color color = style & HIGHLIGHT_BORDER ? Color(255,100,0)
|
||||
: style & HIGHLIGHT_INTERIOR ? Color(255,200,0)
|
||||
: Color(200,170,0);
|
||||
// center
|
||||
RealPoint center = rotation.tr(sym.center);
|
||||
// draw 'spokes'
|
||||
Radians angle = atan2(sym.handle.y, sym.handle.x);
|
||||
dc.SetPen(wxPen(color, sym.kind == SYMMETRY_ROTATION ? 1 : 3));
|
||||
int copies = sym.kind == SYMMETRY_REFLECTION ? sym.copies / 2 * 2 : sym.copies;
|
||||
for (int i = 0; i < copies ; ++i) {
|
||||
Radians a = angle + (i + 0.5) * 2 * M_PI / copies;
|
||||
Vector2D dir(cos(a), sin(a));
|
||||
Vector2D dir2 = rotation.tr(sym.center + 2 * dir);
|
||||
dc.DrawLine(int(center.x), int(center.y), int(dir2.x), int(dir2.y));
|
||||
}
|
||||
// draw center
|
||||
dc.SetPen(*wxBLACK_PEN);
|
||||
dc.SetBrush(color);
|
||||
dc.DrawCircle(int(center.x), int(center.y), sym.kind == SYMMETRY_ROTATION ? 7 : 5);
|
||||
}
|
||||
|
||||
void SymbolViewer::highlightPart(DC& dc, const SymbolGroup& group, HighlightStyle style) {
|
||||
if (style == HIGHLIGHT_BORDER) {
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.SetPen (wxPen(Color(255,0,0), 2));
|
||||
dc.DrawRectangle(rotation.trRectToBB(RealRect(group.bounds)));
|
||||
}
|
||||
FOR_EACH_CONST(part, group.parts) {
|
||||
highlightPart(dc, *part, (HighlightStyle)(style | HIGHLIGHT_LESS));
|
||||
}
|
||||
if (style == HIGHLIGHT_BORDER) {
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.SetPen (wxPen(Color(255,0,0), 2));
|
||||
dc.DrawRectangle(rotation.trRectToBB(RealRect(group.bounds)));
|
||||
}
|
||||
FOR_EACH_CONST(part, group.parts) {
|
||||
highlightPart(dc, *part, (HighlightStyle)(style | HIGHLIGHT_LESS));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SymbolViewer::drawEditingHints(DC& dc) {
|
||||
// TODO?
|
||||
// TODO?
|
||||
}
|
||||
|
||||
@@ -22,79 +22,79 @@ Image render_symbol(const SymbolP& symbol, double border_radius = 0.05, int widt
|
||||
// ----------------------------------------------------------------------------- : Symbol Viewer
|
||||
|
||||
enum HighlightStyle
|
||||
{ HIGHLIGHT_BORDER = 0x01
|
||||
, HIGHLIGHT_INTERIOR = 0x02
|
||||
, HIGHLIGHT_LESS = 0x10
|
||||
, HIGHLIGHT_BORDER_DOT = HIGHLIGHT_BORDER | HIGHLIGHT_LESS
|
||||
{ HIGHLIGHT_BORDER = 0x01
|
||||
, HIGHLIGHT_INTERIOR = 0x02
|
||||
, HIGHLIGHT_LESS = 0x10
|
||||
, HIGHLIGHT_BORDER_DOT = HIGHLIGHT_BORDER | HIGHLIGHT_LESS
|
||||
};
|
||||
|
||||
/// Class that knows how to draw a symbol
|
||||
class SymbolViewer : public SymbolView {
|
||||
public:
|
||||
// --------------------------------------------------- : Data
|
||||
SymbolViewer(const SymbolP& symbol, bool editing_hints, double size = 500, double border_radius = 0.05);
|
||||
|
||||
// drawing
|
||||
double border_radius;
|
||||
bool editing_hints;
|
||||
|
||||
// --------------------------------------------------- : Point translation
|
||||
|
||||
void setZoom(double zoom);
|
||||
void setOrigin(const Vector2D& origin);
|
||||
|
||||
Rotation rotation; ///< Object that handles rotation, scaling and translation
|
||||
Matrix2D multiply; ///< Scaling/rotation of actual parts
|
||||
Vector2D origin; ///< Origin of parts
|
||||
|
||||
// --------------------------------------------------- : Drawing
|
||||
|
||||
/// Draw the symbol to a dc
|
||||
void draw(DC& dc);
|
||||
|
||||
void highlightPart(DC& dc, const SymbolPart& part, HighlightStyle style);
|
||||
void highlightPart(DC& dc, const SymbolShape& shape, HighlightStyle style);
|
||||
void highlightPart(DC& dc, const SymbolSymmetry& sym, HighlightStyle style);
|
||||
void highlightPart(DC& dc, const SymbolGroup& group, HighlightStyle style);
|
||||
|
||||
void drawEditingHints(DC& dc);
|
||||
|
||||
void onAction(const Action&, bool) {}
|
||||
|
||||
|
||||
// --------------------------------------------------- : Data
|
||||
SymbolViewer(const SymbolP& symbol, bool editing_hints, double size = 500, double border_radius = 0.05);
|
||||
|
||||
// drawing
|
||||
double border_radius;
|
||||
bool editing_hints;
|
||||
|
||||
// --------------------------------------------------- : Point translation
|
||||
|
||||
void setZoom(double zoom);
|
||||
void setOrigin(const Vector2D& origin);
|
||||
|
||||
Rotation rotation; ///< Object that handles rotation, scaling and translation
|
||||
Matrix2D multiply; ///< Scaling/rotation of actual parts
|
||||
Vector2D origin; ///< Origin of parts
|
||||
|
||||
// --------------------------------------------------- : Drawing
|
||||
|
||||
/// Draw the symbol to a dc
|
||||
void draw(DC& dc);
|
||||
|
||||
void highlightPart(DC& dc, const SymbolPart& part, HighlightStyle style);
|
||||
void highlightPart(DC& dc, const SymbolShape& shape, HighlightStyle style);
|
||||
void highlightPart(DC& dc, const SymbolSymmetry& sym, HighlightStyle style);
|
||||
void highlightPart(DC& dc, const SymbolGroup& group, HighlightStyle style);
|
||||
|
||||
void drawEditingHints(DC& dc);
|
||||
|
||||
void onAction(const Action&, bool) {}
|
||||
|
||||
|
||||
private:
|
||||
typedef shared_ptr<wxMemoryDC> MemoryDCP;
|
||||
/// Inside a reflection?
|
||||
int in_symmetry;
|
||||
|
||||
/// Combine a symbol part with the dc
|
||||
void combineSymbolPart(DC& dc, const SymbolPart& part, bool& paintedSomething, bool& buffersFilled, bool allow_overlap, MemoryDCP& borderDC, MemoryDCP& interiorDC);
|
||||
|
||||
/// Combines a symbol part with what is currently drawn, the border and interior are drawn separatly
|
||||
/** directB/directI are true if the border/interior is the screen dc, false if it
|
||||
* is a temporary 1 bit one
|
||||
*/
|
||||
void combineSymbolShape(const SymbolShape& part, DC& border, DC& interior, bool directB, bool directI);
|
||||
|
||||
/// Draw a symbol part, draws the border and the interior to separate DCs
|
||||
/** The DCs may be null. directB should be true when drawing the border directly to the screen.
|
||||
* The **Col parameters give the color to use for the (interior of) the border and the interior
|
||||
* default should be white (255) border and black (0) interior.
|
||||
*/
|
||||
void drawSymbolShape(const SymbolShape& shape, DC* border, DC* interior, unsigned char borderCol, unsigned char interiorCol, bool directB, bool oppB);
|
||||
/*
|
||||
// ------------------- Bezier curve calculation
|
||||
|
||||
// Calculate the points on a bezier curve between p0 and p1
|
||||
// Stores the Points in p_out, at most count points are stored
|
||||
// after this call p_out points to just beyond the last point
|
||||
void calcBezierPoint(const ControlPointP& p0, const ControlPointP& p1, wxPoint*& p_out, UInt count);
|
||||
|
||||
// Subdivide a bezier curve by adding at most count points
|
||||
// p0 = c(t0), p1 = c(p1)
|
||||
// subdivides linearly between t0 and t1, and only when necessary
|
||||
// adds points to p_out and increments the pointer when a point is added
|
||||
void calcBezierOpt(const BezierCurve& c, const Vector2D& p0, const Vector2D& p1, double t0, double t1, wxPoint*& p_out, UInt count);
|
||||
typedef shared_ptr<wxMemoryDC> MemoryDCP;
|
||||
/// Inside a reflection?
|
||||
int in_symmetry;
|
||||
|
||||
/// Combine a symbol part with the dc
|
||||
void combineSymbolPart(DC& dc, const SymbolPart& part, bool& paintedSomething, bool& buffersFilled, bool allow_overlap, MemoryDCP& borderDC, MemoryDCP& interiorDC);
|
||||
|
||||
/// Combines a symbol part with what is currently drawn, the border and interior are drawn separatly
|
||||
/** directB/directI are true if the border/interior is the screen dc, false if it
|
||||
* is a temporary 1 bit one
|
||||
*/
|
||||
void combineSymbolShape(const SymbolShape& part, DC& border, DC& interior, bool directB, bool directI);
|
||||
|
||||
/// Draw a symbol part, draws the border and the interior to separate DCs
|
||||
/** The DCs may be null. directB should be true when drawing the border directly to the screen.
|
||||
* The **Col parameters give the color to use for the (interior of) the border and the interior
|
||||
* default should be white (255) border and black (0) interior.
|
||||
*/
|
||||
void drawSymbolShape(const SymbolShape& shape, DC* border, DC* interior, unsigned char borderCol, unsigned char interiorCol, bool directB, bool oppB);
|
||||
/*
|
||||
// ------------------- Bezier curve calculation
|
||||
|
||||
// Calculate the points on a bezier curve between p0 and p1
|
||||
// Stores the Points in p_out, at most count points are stored
|
||||
// after this call p_out points to just beyond the last point
|
||||
void calcBezierPoint(const ControlPointP& p0, const ControlPointP& p1, wxPoint*& p_out, UInt count);
|
||||
|
||||
// Subdivide a bezier curve by adding at most count points
|
||||
// p0 = c(t0), p1 = c(p1)
|
||||
// subdivides linearly between t0 and t1, and only when necessary
|
||||
// adds points to p_out and increments the pointer when a point is added
|
||||
void calcBezierOpt(const BezierCurve& c, const Vector2D& p0, const Vector2D& p1, double t0, double t1, wxPoint*& p_out, UInt count);
|
||||
*/};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
@@ -12,47 +12,47 @@
|
||||
// ----------------------------------------------------------------------------- : CompoundTextElement
|
||||
|
||||
void CompoundTextElement::draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const {
|
||||
elements.draw(dc, scale, rect, xs, what, start, end);
|
||||
elements.draw(dc, scale, rect, xs, what, start, end);
|
||||
}
|
||||
void CompoundTextElement::getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const {
|
||||
elements.getCharInfo(dc, scale, start, end, out);
|
||||
elements.getCharInfo(dc, scale, start, end, out);
|
||||
}
|
||||
double CompoundTextElement::minScale() const {
|
||||
return elements.minScale();
|
||||
return elements.minScale();
|
||||
}
|
||||
double CompoundTextElement::scaleStep() const {
|
||||
return elements.scaleStep();
|
||||
return elements.scaleStep();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : AtomTextElement
|
||||
|
||||
void AtomTextElement::draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const {
|
||||
if (what & DRAW_ACTIVE) {
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetBrush(Color(210,210,210));
|
||||
dc.DrawRectangle(rect);
|
||||
}
|
||||
CompoundTextElement::draw(dc, scale, rect, xs, what, start, end);
|
||||
if (what & DRAW_ACTIVE) {
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetBrush(Color(210,210,210));
|
||||
dc.DrawRectangle(rect);
|
||||
}
|
||||
CompoundTextElement::draw(dc, scale, rect, xs, what, start, end);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : ErrorTextElement
|
||||
|
||||
void ErrorTextElement::draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const {
|
||||
// Draw wavy underline
|
||||
if (what & DRAW_ERRORS) {
|
||||
dc.SetPen(*wxRED_PEN);
|
||||
RealPoint pos = rect.bottomLeft() - dc.trInvS(RealSize(0,2));
|
||||
RealSize dx(dc.trInvS(2), 0), dy(0, dc.trInvS(1));
|
||||
while (pos.x + 1 < rect.right()) {
|
||||
dc.DrawLine(pos - dy, pos + dx + dy);
|
||||
pos += dx;
|
||||
dy = -dy;
|
||||
}
|
||||
if (pos.x < rect.right()) {
|
||||
// final piece
|
||||
dc.DrawLine(pos - dy, pos + dx * 0.5);
|
||||
}
|
||||
}
|
||||
// Draw the contents
|
||||
CompoundTextElement::draw(dc, scale, rect, xs, what, start, end);
|
||||
// Draw wavy underline
|
||||
if (what & DRAW_ERRORS) {
|
||||
dc.SetPen(*wxRED_PEN);
|
||||
RealPoint pos = rect.bottomLeft() - dc.trInvS(RealSize(0,2));
|
||||
RealSize dx(dc.trInvS(2), 0), dy(0, dc.trInvS(1));
|
||||
while (pos.x + 1 < rect.right()) {
|
||||
dc.DrawLine(pos - dy, pos + dx + dy);
|
||||
pos += dx;
|
||||
dy = -dy;
|
||||
}
|
||||
if (pos.x < rect.right()) {
|
||||
// final piece
|
||||
dc.DrawLine(pos - dy, pos + dx * 0.5);
|
||||
}
|
||||
}
|
||||
// Draw the contents
|
||||
CompoundTextElement::draw(dc, scale, rect, xs, what, start, end);
|
||||
}
|
||||
|
||||
+225
-225
@@ -18,258 +18,258 @@ DECLARE_POINTER_TYPE(FontTextElement);
|
||||
// ----------------------------------------------------------------------------- : TextElements
|
||||
|
||||
void TextElements::draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const {
|
||||
FOR_EACH_CONST(e, elements) {
|
||||
size_t start_ = max(start, e->start);
|
||||
size_t end_ = min(end, e->end);
|
||||
if (start_ < end_) {
|
||||
e->draw(dc, scale,
|
||||
RealRect(rect.x + xs[start_-start] - xs[0], rect.y,
|
||||
xs[end_-start] - xs[start_-start], rect.height),
|
||||
xs + start_ - start, what, start_, end_);
|
||||
}
|
||||
if (end <= e->end) return; // nothing can be after this
|
||||
}
|
||||
FOR_EACH_CONST(e, elements) {
|
||||
size_t start_ = max(start, e->start);
|
||||
size_t end_ = min(end, e->end);
|
||||
if (start_ < end_) {
|
||||
e->draw(dc, scale,
|
||||
RealRect(rect.x + xs[start_-start] - xs[0], rect.y,
|
||||
xs[end_-start] - xs[start_-start], rect.height),
|
||||
xs + start_ - start, what, start_, end_);
|
||||
}
|
||||
if (end <= e->end) return; // nothing can be after this
|
||||
}
|
||||
}
|
||||
|
||||
void TextElements::getCharInfo(RotatedDC& dc, double scale, size_t start, size_t end, vector<CharInfo>& out) const {
|
||||
FOR_EACH_CONST(e, elements) {
|
||||
// characters before this element, after the previous
|
||||
while (out.size() < e->start) {
|
||||
out.push_back(CharInfo());
|
||||
}
|
||||
e->getCharInfo(dc, scale, out);
|
||||
}
|
||||
while (out.size() < end) {
|
||||
out.push_back(CharInfo());
|
||||
}
|
||||
FOR_EACH_CONST(e, elements) {
|
||||
// characters before this element, after the previous
|
||||
while (out.size() < e->start) {
|
||||
out.push_back(CharInfo());
|
||||
}
|
||||
e->getCharInfo(dc, scale, out);
|
||||
}
|
||||
while (out.size() < end) {
|
||||
out.push_back(CharInfo());
|
||||
}
|
||||
}
|
||||
|
||||
double TextElements::minScale() const {
|
||||
double m = 0.0001;
|
||||
FOR_EACH_CONST(e, elements) {
|
||||
m = max(m, e->minScale());
|
||||
}
|
||||
return m;
|
||||
double m = 0.0001;
|
||||
FOR_EACH_CONST(e, elements) {
|
||||
m = max(m, e->minScale());
|
||||
}
|
||||
return m;
|
||||
}
|
||||
double TextElements::scaleStep() const {
|
||||
double m = 1;
|
||||
FOR_EACH_CONST(e, elements) {
|
||||
m = min(m, e->scaleStep());
|
||||
}
|
||||
return m;
|
||||
double m = 1;
|
||||
FOR_EACH_CONST(e, elements) {
|
||||
m = min(m, e->scaleStep());
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
// Colors for <atom-param> tags
|
||||
AColor param_colors[] =
|
||||
{ AColor(0,170,0)
|
||||
, AColor(0,0,200)
|
||||
, AColor(200,0,100)
|
||||
, AColor(200,200,0)
|
||||
, AColor(0,170,170)
|
||||
, AColor(200,0,0)
|
||||
};
|
||||
{ AColor(0,170,0)
|
||||
, AColor(0,0,200)
|
||||
, AColor(200,0,100)
|
||||
, AColor(200,200,0)
|
||||
, AColor(0,170,170)
|
||||
, AColor(200,0,0)
|
||||
};
|
||||
const size_t param_colors_count = sizeof(param_colors) / sizeof(param_colors[0]);
|
||||
|
||||
// Helper class for TextElements::fromString, to allow persistent formating state accross recusive calls
|
||||
struct TextElementsFromString {
|
||||
// What formatting is enabled?
|
||||
int bold, italic, symbol;
|
||||
int soft, kwpph, param, line, soft_line;
|
||||
int code, code_kw, code_string, param_ref, error;
|
||||
int param_id;
|
||||
vector<AColor> colors;
|
||||
vector<double> sizes;
|
||||
/// put angle brackets around the text?
|
||||
bool bracket;
|
||||
|
||||
TextElementsFromString()
|
||||
: bold(0), italic(0), symbol(0), soft(0), kwpph(0), param(0), line(0), soft_line(0)
|
||||
, code(0), code_kw(0), code_string(0), param_ref(0), error(0)
|
||||
, param_id(0), bracket(false) {}
|
||||
|
||||
// read TextElements from a string
|
||||
void fromString(TextElements& te, const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx) {
|
||||
te.elements.clear();
|
||||
end = min(end, text.size());
|
||||
size_t text_start = start;
|
||||
// for each character...
|
||||
for (size_t pos = start ; pos < end ; ) {
|
||||
Char c = text.GetChar(pos);
|
||||
if (c == _('<')) {
|
||||
if (text_start < pos) {
|
||||
// text element before this tag?
|
||||
addText(te, text, text_start, pos, style, ctx);
|
||||
}
|
||||
// a (formatting) tag
|
||||
size_t tag_start = pos;
|
||||
pos = skip_tag(text, tag_start);
|
||||
if (is_substr(text, tag_start, _( "<b"))) bold += 1;
|
||||
else if (is_substr(text, tag_start, _("</b"))) bold -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<i"))) italic += 1;
|
||||
else if (is_substr(text, tag_start, _("</i"))) italic -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<sym"))) symbol += 1;
|
||||
else if (is_substr(text, tag_start, _("</sym"))) symbol -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<line"))) line += 1;
|
||||
else if (is_substr(text, tag_start, _("</line"))) line -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<soft-line"))) soft_line += 1;
|
||||
else if (is_substr(text, tag_start, _("</soft-line"))) soft_line -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<sep-soft"))) soft += 1;
|
||||
else if (is_substr(text, tag_start, _("</sep-soft"))) soft -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<soft"))) soft += 1; // must be after <soft-line
|
||||
else if (is_substr(text, tag_start, _("</soft"))) soft -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<atom-kwpph"))) kwpph += 1;
|
||||
else if (is_substr(text, tag_start, _("</atom-kwpph"))) kwpph -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<code-kw"))) code_kw += 1;
|
||||
else if (is_substr(text, tag_start, _("</code-kw"))) code_kw -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<code-str"))) code_string += 1;
|
||||
else if (is_substr(text, tag_start, _("</code-str"))) code_string -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<code"))) code += 1;
|
||||
else if (is_substr(text, tag_start, _("</code"))) code -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<color"))) {
|
||||
size_t colon = text.find_first_of(_(">:"), tag_start);
|
||||
if (colon < pos - 1 && text.GetChar(colon) == _(':')) {
|
||||
AColor c = parse_acolor(text.substr(colon+1, pos-colon-2));
|
||||
if (!c.Ok()) c = style.font.color;
|
||||
colors.push_back(c);
|
||||
}
|
||||
} else if (is_substr(text, tag_start, _("</color"))) {
|
||||
if (!colors.empty()) colors.pop_back();
|
||||
}
|
||||
else if (is_substr(text, tag_start, _( "<size"))) {
|
||||
size_t colon = text.find_first_of(_(">:"), tag_start);
|
||||
if (colon < pos - 1 && text.GetChar(colon) == _(':')) {
|
||||
double size = style.font.size;
|
||||
String v = text.substr(colon+1, pos-colon-2);
|
||||
v.ToDouble(&size);
|
||||
sizes.push_back(size);
|
||||
}
|
||||
} else if (is_substr(text, tag_start, _("</size"))) {
|
||||
if (!sizes.empty()) sizes.pop_back();
|
||||
}
|
||||
else if (is_substr(text, tag_start, _( "<ref-param"))) {
|
||||
// determine the param being referenced
|
||||
// from a tag <ref-param123>
|
||||
if (pos != String::npos) {
|
||||
String ref = text.substr(tag_start + 10, pos - tag_start - 11);
|
||||
long ref_n;
|
||||
if (ref.ToLong(&ref_n)) {
|
||||
param_id = (ref_n - 1)%param_colors_count + param_colors_count;
|
||||
}
|
||||
}
|
||||
param_ref += 1;
|
||||
}
|
||||
else if (is_substr(text, tag_start, _("</ref-param"))) param_ref -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<atom-param"))) param += 1;
|
||||
else if (is_substr(text, tag_start, _("</atom-param"))) param -= 1;
|
||||
else if (is_substr(text, tag_start, _("<atom"))) {
|
||||
// 'atomic' indicator
|
||||
size_t end_tag = min(end, match_close_tag(text, tag_start));
|
||||
intrusive_ptr<AtomTextElement> e(new AtomTextElement(pos, end_tag));
|
||||
fromString(e->elements, text, pos, end_tag, style, ctx);
|
||||
te.elements.push_back(e);
|
||||
pos = skip_tag(text, end_tag);
|
||||
} else if (is_substr(text, tag_start, _( "<error"))) {
|
||||
// error indicator
|
||||
size_t end_tag = min(end, match_close_tag(text, tag_start));
|
||||
intrusive_ptr<ErrorTextElement> e(new ErrorTextElement(pos, end_tag));
|
||||
fromString(e->elements, text, pos, end_tag, style, ctx);
|
||||
te.elements.push_back(e);
|
||||
pos = skip_tag(text, end_tag);
|
||||
} else {
|
||||
// ignore other tags
|
||||
}
|
||||
// text starts again after the tag
|
||||
text_start = pos;
|
||||
} else {
|
||||
// normal text
|
||||
pos += 1;
|
||||
}
|
||||
}
|
||||
if (text_start < end) {
|
||||
// remaining text at the end
|
||||
addText(te, text, text_start, end, style, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// What formatting is enabled?
|
||||
int bold, italic, symbol;
|
||||
int soft, kwpph, param, line, soft_line;
|
||||
int code, code_kw, code_string, param_ref, error;
|
||||
int param_id;
|
||||
vector<AColor> colors;
|
||||
vector<double> sizes;
|
||||
/// put angle brackets around the text?
|
||||
bool bracket;
|
||||
|
||||
TextElementsFromString()
|
||||
: bold(0), italic(0), symbol(0), soft(0), kwpph(0), param(0), line(0), soft_line(0)
|
||||
, code(0), code_kw(0), code_string(0), param_ref(0), error(0)
|
||||
, param_id(0), bracket(false) {}
|
||||
|
||||
// read TextElements from a string
|
||||
void fromString(TextElements& te, const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx) {
|
||||
te.elements.clear();
|
||||
end = min(end, text.size());
|
||||
size_t text_start = start;
|
||||
// for each character...
|
||||
for (size_t pos = start ; pos < end ; ) {
|
||||
Char c = text.GetChar(pos);
|
||||
if (c == _('<')) {
|
||||
if (text_start < pos) {
|
||||
// text element before this tag?
|
||||
addText(te, text, text_start, pos, style, ctx);
|
||||
}
|
||||
// a (formatting) tag
|
||||
size_t tag_start = pos;
|
||||
pos = skip_tag(text, tag_start);
|
||||
if (is_substr(text, tag_start, _( "<b"))) bold += 1;
|
||||
else if (is_substr(text, tag_start, _("</b"))) bold -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<i"))) italic += 1;
|
||||
else if (is_substr(text, tag_start, _("</i"))) italic -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<sym"))) symbol += 1;
|
||||
else if (is_substr(text, tag_start, _("</sym"))) symbol -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<line"))) line += 1;
|
||||
else if (is_substr(text, tag_start, _("</line"))) line -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<soft-line"))) soft_line += 1;
|
||||
else if (is_substr(text, tag_start, _("</soft-line"))) soft_line -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<sep-soft"))) soft += 1;
|
||||
else if (is_substr(text, tag_start, _("</sep-soft"))) soft -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<soft"))) soft += 1; // must be after <soft-line
|
||||
else if (is_substr(text, tag_start, _("</soft"))) soft -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<atom-kwpph"))) kwpph += 1;
|
||||
else if (is_substr(text, tag_start, _("</atom-kwpph"))) kwpph -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<code-kw"))) code_kw += 1;
|
||||
else if (is_substr(text, tag_start, _("</code-kw"))) code_kw -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<code-str"))) code_string += 1;
|
||||
else if (is_substr(text, tag_start, _("</code-str"))) code_string -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<code"))) code += 1;
|
||||
else if (is_substr(text, tag_start, _("</code"))) code -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<color"))) {
|
||||
size_t colon = text.find_first_of(_(">:"), tag_start);
|
||||
if (colon < pos - 1 && text.GetChar(colon) == _(':')) {
|
||||
AColor c = parse_acolor(text.substr(colon+1, pos-colon-2));
|
||||
if (!c.Ok()) c = style.font.color;
|
||||
colors.push_back(c);
|
||||
}
|
||||
} else if (is_substr(text, tag_start, _("</color"))) {
|
||||
if (!colors.empty()) colors.pop_back();
|
||||
}
|
||||
else if (is_substr(text, tag_start, _( "<size"))) {
|
||||
size_t colon = text.find_first_of(_(">:"), tag_start);
|
||||
if (colon < pos - 1 && text.GetChar(colon) == _(':')) {
|
||||
double size = style.font.size;
|
||||
String v = text.substr(colon+1, pos-colon-2);
|
||||
v.ToDouble(&size);
|
||||
sizes.push_back(size);
|
||||
}
|
||||
} else if (is_substr(text, tag_start, _("</size"))) {
|
||||
if (!sizes.empty()) sizes.pop_back();
|
||||
}
|
||||
else if (is_substr(text, tag_start, _( "<ref-param"))) {
|
||||
// determine the param being referenced
|
||||
// from a tag <ref-param123>
|
||||
if (pos != String::npos) {
|
||||
String ref = text.substr(tag_start + 10, pos - tag_start - 11);
|
||||
long ref_n;
|
||||
if (ref.ToLong(&ref_n)) {
|
||||
param_id = (ref_n - 1)%param_colors_count + param_colors_count;
|
||||
}
|
||||
}
|
||||
param_ref += 1;
|
||||
}
|
||||
else if (is_substr(text, tag_start, _("</ref-param"))) param_ref -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<atom-param"))) param += 1;
|
||||
else if (is_substr(text, tag_start, _("</atom-param"))) param -= 1;
|
||||
else if (is_substr(text, tag_start, _("<atom"))) {
|
||||
// 'atomic' indicator
|
||||
size_t end_tag = min(end, match_close_tag(text, tag_start));
|
||||
intrusive_ptr<AtomTextElement> e(new AtomTextElement(pos, end_tag));
|
||||
fromString(e->elements, text, pos, end_tag, style, ctx);
|
||||
te.elements.push_back(e);
|
||||
pos = skip_tag(text, end_tag);
|
||||
} else if (is_substr(text, tag_start, _( "<error"))) {
|
||||
// error indicator
|
||||
size_t end_tag = min(end, match_close_tag(text, tag_start));
|
||||
intrusive_ptr<ErrorTextElement> e(new ErrorTextElement(pos, end_tag));
|
||||
fromString(e->elements, text, pos, end_tag, style, ctx);
|
||||
te.elements.push_back(e);
|
||||
pos = skip_tag(text, end_tag);
|
||||
} else {
|
||||
// ignore other tags
|
||||
}
|
||||
// text starts again after the tag
|
||||
text_start = pos;
|
||||
} else {
|
||||
// normal text
|
||||
pos += 1;
|
||||
}
|
||||
}
|
||||
if (text_start < end) {
|
||||
// remaining text at the end
|
||||
addText(te, text, text_start, end, style, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/// Create a text element for a piece of text, text[start..end)
|
||||
void addText(TextElements& te, const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx) {
|
||||
String content = untag(text.substr(start, end - start));
|
||||
assert(content.size() == end-start);
|
||||
// use symbol font?
|
||||
if (symbol > 0 && style.symbol_font.valid()) {
|
||||
te.elements.push_back(intrusive(new SymbolTextElement(content, start, end, style.symbol_font, &ctx)));
|
||||
} else {
|
||||
// text, possibly mixed with symbols
|
||||
DrawWhat what = soft > 0 ? DRAW_ACTIVE : DRAW_NORMAL;
|
||||
LineBreak line_break = line > 0 ? BREAK_LINE :
|
||||
soft_line > 0 ? BREAK_SOFT : BREAK_HARD;
|
||||
if (kwpph > 0 || param > 0) {
|
||||
// bracket the text
|
||||
content = String(LEFT_ANGLE_BRACKET) + content + RIGHT_ANGLE_BRACKET;
|
||||
start -= 1;
|
||||
end += 1;
|
||||
}
|
||||
if (style.always_symbol && style.symbol_font.valid()) {
|
||||
// mixed symbols/text, autodetected by symbol font
|
||||
size_t text_pos = 0;
|
||||
size_t pos = 0;
|
||||
FontP font;
|
||||
while (pos < end-start) {
|
||||
if (size_t n = style.symbol_font.font->recognizePrefix(content,pos)) {
|
||||
// at 'pos' there are n symbol font characters
|
||||
if (text_pos < pos) {
|
||||
// text before it?
|
||||
if (!font) font = makeFont(style);
|
||||
te.elements.push_back(intrusive(new FontTextElement(content.substr(text_pos, pos-text_pos), start+text_pos, start+pos, font, what, line_break)));
|
||||
}
|
||||
te.elements.push_back(intrusive(new SymbolTextElement(content.substr(pos,n), start+pos, start+pos+n, style.symbol_font, &ctx)));
|
||||
text_pos = pos += n;
|
||||
} else {
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
if (text_pos < pos) {
|
||||
if (!font) font = makeFont(style);
|
||||
te.elements.push_back(intrusive(new FontTextElement(content.substr(text_pos), start+text_pos, end, font, what, line_break)));
|
||||
}
|
||||
} else {
|
||||
te.elements.push_back(intrusive(new FontTextElement(content, start, end, makeFont(style), what, line_break)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FontP makeFont(const TextStyle& style) {
|
||||
return style.font.make(
|
||||
(bold > 0 ? FONT_BOLD : FONT_NORMAL) |
|
||||
(italic > 0 ? FONT_ITALIC : FONT_NORMAL) |
|
||||
(soft > 0 ? FONT_SOFT : FONT_NORMAL) |
|
||||
(kwpph > 0 ? FONT_SOFT : FONT_NORMAL) |
|
||||
(code > 0 ? FONT_CODE : FONT_NORMAL) |
|
||||
(code_kw > 0 ? FONT_CODE_KW : FONT_NORMAL) |
|
||||
(code_string > 0 ? FONT_CODE_STRING : FONT_NORMAL),
|
||||
param > 0 || param_ref > 0
|
||||
? ¶m_colors[(param_id++) % param_colors_count]
|
||||
: !colors.empty()
|
||||
? &colors.back()
|
||||
: nullptr,
|
||||
!sizes.empty() ? &sizes.back() : nullptr
|
||||
);
|
||||
}
|
||||
/// Create a text element for a piece of text, text[start..end)
|
||||
void addText(TextElements& te, const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx) {
|
||||
String content = untag(text.substr(start, end - start));
|
||||
assert(content.size() == end-start);
|
||||
// use symbol font?
|
||||
if (symbol > 0 && style.symbol_font.valid()) {
|
||||
te.elements.push_back(intrusive(new SymbolTextElement(content, start, end, style.symbol_font, &ctx)));
|
||||
} else {
|
||||
// text, possibly mixed with symbols
|
||||
DrawWhat what = soft > 0 ? DRAW_ACTIVE : DRAW_NORMAL;
|
||||
LineBreak line_break = line > 0 ? BREAK_LINE :
|
||||
soft_line > 0 ? BREAK_SOFT : BREAK_HARD;
|
||||
if (kwpph > 0 || param > 0) {
|
||||
// bracket the text
|
||||
content = String(LEFT_ANGLE_BRACKET) + content + RIGHT_ANGLE_BRACKET;
|
||||
start -= 1;
|
||||
end += 1;
|
||||
}
|
||||
if (style.always_symbol && style.symbol_font.valid()) {
|
||||
// mixed symbols/text, autodetected by symbol font
|
||||
size_t text_pos = 0;
|
||||
size_t pos = 0;
|
||||
FontP font;
|
||||
while (pos < end-start) {
|
||||
if (size_t n = style.symbol_font.font->recognizePrefix(content,pos)) {
|
||||
// at 'pos' there are n symbol font characters
|
||||
if (text_pos < pos) {
|
||||
// text before it?
|
||||
if (!font) font = makeFont(style);
|
||||
te.elements.push_back(intrusive(new FontTextElement(content.substr(text_pos, pos-text_pos), start+text_pos, start+pos, font, what, line_break)));
|
||||
}
|
||||
te.elements.push_back(intrusive(new SymbolTextElement(content.substr(pos,n), start+pos, start+pos+n, style.symbol_font, &ctx)));
|
||||
text_pos = pos += n;
|
||||
} else {
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
if (text_pos < pos) {
|
||||
if (!font) font = makeFont(style);
|
||||
te.elements.push_back(intrusive(new FontTextElement(content.substr(text_pos), start+text_pos, end, font, what, line_break)));
|
||||
}
|
||||
} else {
|
||||
te.elements.push_back(intrusive(new FontTextElement(content, start, end, makeFont(style), what, line_break)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FontP makeFont(const TextStyle& style) {
|
||||
return style.font.make(
|
||||
(bold > 0 ? FONT_BOLD : FONT_NORMAL) |
|
||||
(italic > 0 ? FONT_ITALIC : FONT_NORMAL) |
|
||||
(soft > 0 ? FONT_SOFT : FONT_NORMAL) |
|
||||
(kwpph > 0 ? FONT_SOFT : FONT_NORMAL) |
|
||||
(code > 0 ? FONT_CODE : FONT_NORMAL) |
|
||||
(code_kw > 0 ? FONT_CODE_KW : FONT_NORMAL) |
|
||||
(code_string > 0 ? FONT_CODE_STRING : FONT_NORMAL),
|
||||
param > 0 || param_ref > 0
|
||||
? ¶m_colors[(param_id++) % param_colors_count]
|
||||
: !colors.empty()
|
||||
? &colors.back()
|
||||
: nullptr,
|
||||
!sizes.empty() ? &sizes.back() : nullptr
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
void TextElements::fromString(const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx) {
|
||||
TextElementsFromString f;
|
||||
f.fromString(*this, text, start, end, style, ctx);
|
||||
TextElementsFromString f;
|
||||
f.fromString(*this, text, start, end, style, ctx);
|
||||
}
|
||||
/*
|
||||
// ----------------------------------------------------------------------------- : CompoundTextElement
|
||||
|
||||
void CompoundTextElement::draw(RotatedDC& dc, double scale, const RealRect& rect, DrawWhat what, size_t start, size_t end) const {
|
||||
elements.draw(dc, scale, rect, what, start, end);
|
||||
elements.draw(dc, scale, rect, what, start, end);
|
||||
}
|
||||
RealSize CompoundTextElement::charSize(RotatedDC& dc, double scale, size_t index) const {
|
||||
return elements.charSize(rot, scale, index);
|
||||
return elements.charSize(rot, scale, index);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
+93
-93
@@ -25,47 +25,47 @@ class SymbolFontRef;
|
||||
|
||||
/// Information on a linebreak
|
||||
enum LineBreak
|
||||
{ BREAK_NO // no line break ever
|
||||
, BREAK_MAYBE // break here when in "direction:vertical" mode
|
||||
, BREAK_SPACE // optional line break (' ')
|
||||
, BREAK_SOFT // always a line break, spacing as a soft break
|
||||
, BREAK_HARD // always a line break ('\n')
|
||||
, BREAK_LINE // line break with a separator line (<line>)
|
||||
{ BREAK_NO // no line break ever
|
||||
, BREAK_MAYBE // break here when in "direction:vertical" mode
|
||||
, BREAK_SPACE // optional line break (' ')
|
||||
, BREAK_SOFT // always a line break, spacing as a soft break
|
||||
, BREAK_HARD // always a line break ('\n')
|
||||
, BREAK_LINE // line break with a separator line (<line>)
|
||||
};
|
||||
|
||||
/// Information on a character in a TextElement
|
||||
struct CharInfo {
|
||||
RealSize size; ///< Size of this character
|
||||
LineBreak break_after : 31; ///< How/when to break after it?
|
||||
bool soft : 1; ///< Is this a 'soft' character? soft characters are ignored for alignment
|
||||
|
||||
explicit CharInfo()
|
||||
: break_after(BREAK_NO), soft(true)
|
||||
{}
|
||||
inline CharInfo(RealSize size, LineBreak break_after, bool soft = false)
|
||||
: size(size), break_after(break_after), soft(soft)
|
||||
{}
|
||||
RealSize size; ///< Size of this character
|
||||
LineBreak break_after : 31; ///< How/when to break after it?
|
||||
bool soft : 1; ///< Is this a 'soft' character? soft characters are ignored for alignment
|
||||
|
||||
explicit CharInfo()
|
||||
: break_after(BREAK_NO), soft(true)
|
||||
{}
|
||||
inline CharInfo(RealSize size, LineBreak break_after, bool soft = false)
|
||||
: size(size), break_after(break_after), soft(soft)
|
||||
{}
|
||||
};
|
||||
|
||||
/// A section of text that can be rendered using a TextViewer
|
||||
class TextElement : public IntrusivePtrBase<TextElement> {
|
||||
public:
|
||||
/// What section of the input string is this element?
|
||||
size_t start, end;
|
||||
|
||||
inline TextElement(size_t start ,size_t end) : start(start), end(end) {}
|
||||
virtual ~TextElement() {}
|
||||
|
||||
/// Draw a subsection section of the text in the given rectangle
|
||||
/** xs give the x coordinates for each character
|
||||
* this->start <= start < end <= this->end <= text.size() */
|
||||
virtual void draw (RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const = 0;
|
||||
/// Get information on all characters in the range [start...end) and store them in out
|
||||
virtual void getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const = 0;
|
||||
/// Return the minimum scale factor allowed (starts at 1)
|
||||
virtual double minScale() const = 0;
|
||||
/// Return the steps the scale factor should take
|
||||
virtual double scaleStep() const = 0;
|
||||
/// What section of the input string is this element?
|
||||
size_t start, end;
|
||||
|
||||
inline TextElement(size_t start ,size_t end) : start(start), end(end) {}
|
||||
virtual ~TextElement() {}
|
||||
|
||||
/// Draw a subsection section of the text in the given rectangle
|
||||
/** xs give the x coordinates for each character
|
||||
* this->start <= start < end <= this->end <= text.size() */
|
||||
virtual void draw (RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const = 0;
|
||||
/// Get information on all characters in the range [start...end) and store them in out
|
||||
virtual void getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const = 0;
|
||||
/// Return the minimum scale factor allowed (starts at 1)
|
||||
virtual double minScale() const = 0;
|
||||
/// Return the steps the scale factor should take
|
||||
virtual double scaleStep() const = 0;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : TextElements
|
||||
@@ -73,26 +73,26 @@ class TextElement : public IntrusivePtrBase<TextElement> {
|
||||
/// A list of text elements
|
||||
class TextElements {
|
||||
public:
|
||||
/// Draw all the elements (as need to show the range start..end)
|
||||
void draw (RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
|
||||
// Get information on all characters in the range [start...end) and store them in out
|
||||
void getCharInfo(RotatedDC& dc, double scale, size_t start, size_t end, vector<CharInfo>& out) const;
|
||||
/// Return the minimum scale factor allowed by all elements
|
||||
double minScale() const;
|
||||
/// Return the steps the scale factor should take
|
||||
double scaleStep() const;
|
||||
|
||||
/// The actual elements
|
||||
/** They must be in order of positions and not overlap, i.e.
|
||||
* i < j ==> elements[i].end <= elements[j].start
|
||||
*/
|
||||
vector<TextElementP> elements;
|
||||
|
||||
/// Find the element that contains the given index, if there is any
|
||||
vector<TextElementP>::const_iterator findByIndex(size_t index) const;
|
||||
|
||||
/// Read the elements from a string
|
||||
void fromString(const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx);
|
||||
/// Draw all the elements (as need to show the range start..end)
|
||||
void draw (RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
|
||||
// Get information on all characters in the range [start...end) and store them in out
|
||||
void getCharInfo(RotatedDC& dc, double scale, size_t start, size_t end, vector<CharInfo>& out) const;
|
||||
/// Return the minimum scale factor allowed by all elements
|
||||
double minScale() const;
|
||||
/// Return the steps the scale factor should take
|
||||
double scaleStep() const;
|
||||
|
||||
/// The actual elements
|
||||
/** They must be in order of positions and not overlap, i.e.
|
||||
* i < j ==> elements[i].end <= elements[j].start
|
||||
*/
|
||||
vector<TextElementP> elements;
|
||||
|
||||
/// Find the element that contains the given index, if there is any
|
||||
vector<TextElementP>::const_iterator findByIndex(size_t index) const;
|
||||
|
||||
/// Read the elements from a string
|
||||
void fromString(const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : SimpleTextElement
|
||||
@@ -100,45 +100,45 @@ class TextElements {
|
||||
/// A text element that just shows text
|
||||
class SimpleTextElement : public TextElement {
|
||||
public:
|
||||
SimpleTextElement(const String& content, size_t start, size_t end)
|
||||
: TextElement(start, end), content(content)
|
||||
{}
|
||||
String content; ///< Text to show
|
||||
SimpleTextElement(const String& content, size_t start, size_t end)
|
||||
: TextElement(start, end), content(content)
|
||||
{}
|
||||
String content; ///< Text to show
|
||||
};
|
||||
|
||||
/// A text element that uses a normal font
|
||||
class FontTextElement : public SimpleTextElement {
|
||||
public:
|
||||
FontTextElement(const String& content, size_t start, size_t end, const FontP& font, DrawWhat draw_as, LineBreak break_style)
|
||||
: SimpleTextElement(content, start, end)
|
||||
, font(font), draw_as(draw_as), break_style(break_style)
|
||||
{}
|
||||
|
||||
virtual void draw (RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
|
||||
virtual void getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const;
|
||||
virtual double minScale() const;
|
||||
virtual double scaleStep() const;
|
||||
FontTextElement(const String& content, size_t start, size_t end, const FontP& font, DrawWhat draw_as, LineBreak break_style)
|
||||
: SimpleTextElement(content, start, end)
|
||||
, font(font), draw_as(draw_as), break_style(break_style)
|
||||
{}
|
||||
|
||||
virtual void draw (RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
|
||||
virtual void getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const;
|
||||
virtual double minScale() const;
|
||||
virtual double scaleStep() const;
|
||||
private:
|
||||
FontP font;
|
||||
DrawWhat draw_as;
|
||||
LineBreak break_style;
|
||||
FontP font;
|
||||
DrawWhat draw_as;
|
||||
LineBreak break_style;
|
||||
};
|
||||
|
||||
/// A text element that uses a symbol font
|
||||
class SymbolTextElement : public SimpleTextElement {
|
||||
public:
|
||||
SymbolTextElement(const String& content, size_t start, size_t end, const SymbolFontRef& font, Context* ctx)
|
||||
: SimpleTextElement(content, start, end)
|
||||
, font(font), ctx(*ctx)
|
||||
{}
|
||||
|
||||
virtual void draw (RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
|
||||
virtual void getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const;
|
||||
virtual double minScale() const;
|
||||
virtual double scaleStep() const;
|
||||
SymbolTextElement(const String& content, size_t start, size_t end, const SymbolFontRef& font, Context* ctx)
|
||||
: SimpleTextElement(content, start, end)
|
||||
, font(font), ctx(*ctx)
|
||||
{}
|
||||
|
||||
virtual void draw (RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
|
||||
virtual void getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const;
|
||||
virtual double minScale() const;
|
||||
virtual double scaleStep() const;
|
||||
private:
|
||||
const SymbolFontRef& font; // owned by TextStyle
|
||||
Context& ctx;
|
||||
const SymbolFontRef& font; // owned by TextStyle
|
||||
Context& ctx;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : CompoundTextElement
|
||||
@@ -146,30 +146,30 @@ class SymbolTextElement : public SimpleTextElement {
|
||||
/// A TextElement consisting of sub elements
|
||||
class CompoundTextElement : public TextElement {
|
||||
public:
|
||||
CompoundTextElement(size_t start, size_t end) : TextElement(start, end) {}
|
||||
|
||||
virtual void draw (RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
|
||||
virtual void getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const;
|
||||
virtual double minScale() const;
|
||||
virtual double scaleStep() const;
|
||||
|
||||
TextElements elements; ///< the elements
|
||||
CompoundTextElement(size_t start, size_t end) : TextElement(start, end) {}
|
||||
|
||||
virtual void draw (RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
|
||||
virtual void getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const;
|
||||
virtual double minScale() const;
|
||||
virtual double scaleStep() const;
|
||||
|
||||
TextElements elements; ///< the elements
|
||||
};
|
||||
|
||||
/// A TextElement drawn using a grey background
|
||||
class AtomTextElement : public CompoundTextElement {
|
||||
public:
|
||||
AtomTextElement(size_t start, size_t end) : CompoundTextElement(start, end) {}
|
||||
|
||||
virtual void draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
|
||||
AtomTextElement(size_t start, size_t end) : CompoundTextElement(start, end) {}
|
||||
|
||||
virtual void draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
|
||||
};
|
||||
|
||||
/// A TextElement drawn using a red wavy underline
|
||||
class ErrorTextElement : public CompoundTextElement {
|
||||
public:
|
||||
ErrorTextElement(size_t start, size_t end) : CompoundTextElement(start, end) {}
|
||||
|
||||
virtual void draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
|
||||
ErrorTextElement(size_t start, size_t end) : CompoundTextElement(start, end) {}
|
||||
|
||||
virtual void draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+32
-32
@@ -13,44 +13,44 @@
|
||||
// ----------------------------------------------------------------------------- : FontTextElement
|
||||
|
||||
void FontTextElement::draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const {
|
||||
if ((what & draw_as) != draw_as) return; // don't draw
|
||||
// text
|
||||
String text = content.substr(start - this->start, end - start);
|
||||
if (!text.empty() && text.GetChar(text.size() - 1) == _('\n')) {
|
||||
text = text.substr(0, text.size() - 1); // don't draw last \n
|
||||
}
|
||||
// draw
|
||||
dc.SetFont(*font, scale);
|
||||
dc.DrawTextWithShadow(text, *font, rect.position());
|
||||
if ((what & draw_as) != draw_as) return; // don't draw
|
||||
// text
|
||||
String text = content.substr(start - this->start, end - start);
|
||||
if (!text.empty() && text.GetChar(text.size() - 1) == _('\n')) {
|
||||
text = text.substr(0, text.size() - 1); // don't draw last \n
|
||||
}
|
||||
// draw
|
||||
dc.SetFont(*font, scale);
|
||||
dc.DrawTextWithShadow(text, *font, rect.position());
|
||||
}
|
||||
|
||||
void FontTextElement::getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const {
|
||||
// font
|
||||
dc.SetFont(*font, scale);
|
||||
// find sizes & breaks
|
||||
double prev_width = 0;
|
||||
size_t line_start = start; // start of the current line
|
||||
for (size_t i = start ; i < end ; ++i) {
|
||||
Char c = content.GetChar(i - this->start);
|
||||
if (c == _('\n')) {
|
||||
out.push_back(CharInfo(RealSize(0, dc.GetCharHeight()), break_style, draw_as == DRAW_ACTIVE));
|
||||
line_start = i + 1;
|
||||
prev_width = 0;
|
||||
} else {
|
||||
RealSize s = dc.GetTextExtent(content.substr(line_start - this->start, i - line_start + 1));
|
||||
out.push_back(CharInfo(
|
||||
RealSize(s.width - prev_width, s.height),
|
||||
c == _(' ') ? BREAK_SPACE : BREAK_MAYBE,
|
||||
draw_as == DRAW_ACTIVE // from <soft> tag
|
||||
));
|
||||
prev_width = s.width;
|
||||
}
|
||||
}
|
||||
// font
|
||||
dc.SetFont(*font, scale);
|
||||
// find sizes & breaks
|
||||
double prev_width = 0;
|
||||
size_t line_start = start; // start of the current line
|
||||
for (size_t i = start ; i < end ; ++i) {
|
||||
Char c = content.GetChar(i - this->start);
|
||||
if (c == _('\n')) {
|
||||
out.push_back(CharInfo(RealSize(0, dc.GetCharHeight()), break_style, draw_as == DRAW_ACTIVE));
|
||||
line_start = i + 1;
|
||||
prev_width = 0;
|
||||
} else {
|
||||
RealSize s = dc.GetTextExtent(content.substr(line_start - this->start, i - line_start + 1));
|
||||
out.push_back(CharInfo(
|
||||
RealSize(s.width - prev_width, s.height),
|
||||
c == _(' ') ? BREAK_SPACE : BREAK_MAYBE,
|
||||
draw_as == DRAW_ACTIVE // from <soft> tag
|
||||
));
|
||||
prev_width = s.width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double FontTextElement::minScale() const {
|
||||
return min(font->size(), font->scale_down_to) / max(0.01, font->size());
|
||||
return min(font->size(), font->scale_down_to) / max(0.01, font->size());
|
||||
}
|
||||
double FontTextElement::scaleStep() const {
|
||||
return 1. / max(font->size() * 4, 1.);
|
||||
return 1. / max(font->size() * 4, 1.);
|
||||
}
|
||||
|
||||
@@ -13,21 +13,21 @@
|
||||
// ----------------------------------------------------------------------------- : SymbolTextElement
|
||||
|
||||
void SymbolTextElement::draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const {
|
||||
if (!(what & DRAW_NORMAL)) return;
|
||||
if (font.font) {
|
||||
font.font->draw(dc, ctx, rect, font.size * scale, font.alignment, content.substr(start - this->start, end-start));
|
||||
}
|
||||
if (!(what & DRAW_NORMAL)) return;
|
||||
if (font.font) {
|
||||
font.font->draw(dc, ctx, rect, font.size * scale, font.alignment, content.substr(start - this->start, end-start));
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolTextElement::getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const {
|
||||
if (font.font) {
|
||||
font.font->getCharInfo(dc, ctx, font.size * scale, content.substr(start - this->start, end-start), out);
|
||||
}
|
||||
if (font.font) {
|
||||
font.font->getCharInfo(dc, ctx, font.size * scale, content.substr(start - this->start, end-start), out);
|
||||
}
|
||||
}
|
||||
|
||||
double SymbolTextElement::minScale() const {
|
||||
return min(font.size(), font.scale_down_to) / max(0.01, font.size());
|
||||
return min(font.size(), font.scale_down_to) / max(0.01, font.size());
|
||||
}
|
||||
double SymbolTextElement::scaleStep() const {
|
||||
return 1. / max(font.size * 4, 1.);
|
||||
return 1. / max(font.size * 4, 1.);
|
||||
}
|
||||
|
||||
+664
-664
File diff suppressed because it is too large
Load Diff
+116
-116
@@ -35,124 +35,124 @@
|
||||
*/
|
||||
class TextViewer {
|
||||
public:
|
||||
/// Information on a line in the textbox
|
||||
struct Line;
|
||||
|
||||
TextViewer();
|
||||
~TextViewer();
|
||||
|
||||
// --------------------------------------------------- : Drawing
|
||||
|
||||
/// Draw the given text with the given text style
|
||||
/** The drawing information is cached,
|
||||
* before calling draw again with different text/style reset() should be called
|
||||
*/
|
||||
void draw(RotatedDC& dc, const TextStyle& style, DrawWhat what);
|
||||
/// Draw an indicator for selected text
|
||||
void drawSelection(RotatedDC& dc, const TextStyle& style, size_t sel_start, size_t sel_end);
|
||||
/// Draw separators for <line> tags
|
||||
void drawSeparators(RotatedDC& dc);
|
||||
|
||||
/// Prepare the text for drawing, if it is not already prepared
|
||||
/** Returns true if something has been done */
|
||||
bool prepare(RotatedDC& dc, const String& text, TextStyle& style, Context&);
|
||||
/// Reset the cached data, at a new call to draw it will be recalculated
|
||||
/** If related, the new value is related to the old one, and layout information should be reused where possible
|
||||
*/
|
||||
void reset(bool related);
|
||||
/// Is the viewer prepare()d?
|
||||
bool prepared() const;
|
||||
|
||||
// --------------------------------------------------- : Positions
|
||||
|
||||
/// Find the character index that is on a line above/below index
|
||||
/** If this would move outisde the text, returns the input index */
|
||||
size_t moveLine(size_t index, int delta) const;
|
||||
|
||||
/// The character index of the start of the line that character #index is on
|
||||
size_t lineStart(size_t index) const;
|
||||
/// The character index past the end of the line that character #index is on
|
||||
size_t lineEnd (size_t index) const;
|
||||
|
||||
/// Find the index of the character at the given position
|
||||
/** If the position is before everything returns 0,
|
||||
* if it is after everything returns text.size().
|
||||
* The position is in internal coordinates */
|
||||
size_t indexAt(const RealPoint& pos) const;
|
||||
/// Find the position of the character at the given index
|
||||
/** The position is in internal coordinates */
|
||||
RealPoint posOf(size_t index) const;
|
||||
|
||||
/// Return the rectangle around a single character
|
||||
/** If 'first' prefers earlier lines */
|
||||
RealRect charRect(size_t index, bool first) const;
|
||||
/// Is the character at the given index visible?
|
||||
bool isVisible(size_t index) const;
|
||||
/// Find the first character index that is at/before/after the given index, and which has a nonzero width
|
||||
/** More precisely: it returns a position so that the character after it in the direction delta has nonzero width
|
||||
*/
|
||||
size_t firstVisibleChar(size_t index, int delta) const;
|
||||
|
||||
/// Return the height of the last line
|
||||
double heightOfLastLine() const;
|
||||
|
||||
// --------------------------------------------------- : Lines/scrolling
|
||||
|
||||
/// The total number of lines
|
||||
size_t lineCount() const;
|
||||
/// number of fully visible lines, height gives the height of the box
|
||||
size_t visibleLineCount(double height) const;
|
||||
/// the index of the first visible line
|
||||
size_t firstVisibleLine() const;
|
||||
// scroll so line_id becomes the first visible line
|
||||
void scrollTo(size_t line_id);
|
||||
/// Ensure the specified character is fully visible
|
||||
/* Always scrolls by a whole line.
|
||||
* Returns true if the editor has scrolled.
|
||||
*/
|
||||
bool ensureVisible(double height, size_t char_id);
|
||||
|
||||
/// Get exact scroll position
|
||||
double getExactScrollPosition() const;
|
||||
/// Set exact scroll position
|
||||
void setExactScrollPosition(double pos);
|
||||
|
||||
/// Information on a line in the textbox
|
||||
struct Line;
|
||||
|
||||
TextViewer();
|
||||
~TextViewer();
|
||||
|
||||
// --------------------------------------------------- : Drawing
|
||||
|
||||
/// Draw the given text with the given text style
|
||||
/** The drawing information is cached,
|
||||
* before calling draw again with different text/style reset() should be called
|
||||
*/
|
||||
void draw(RotatedDC& dc, const TextStyle& style, DrawWhat what);
|
||||
/// Draw an indicator for selected text
|
||||
void drawSelection(RotatedDC& dc, const TextStyle& style, size_t sel_start, size_t sel_end);
|
||||
/// Draw separators for <line> tags
|
||||
void drawSeparators(RotatedDC& dc);
|
||||
|
||||
/// Prepare the text for drawing, if it is not already prepared
|
||||
/** Returns true if something has been done */
|
||||
bool prepare(RotatedDC& dc, const String& text, TextStyle& style, Context&);
|
||||
/// Reset the cached data, at a new call to draw it will be recalculated
|
||||
/** If related, the new value is related to the old one, and layout information should be reused where possible
|
||||
*/
|
||||
void reset(bool related);
|
||||
/// Is the viewer prepare()d?
|
||||
bool prepared() const;
|
||||
|
||||
// --------------------------------------------------- : Positions
|
||||
|
||||
/// Find the character index that is on a line above/below index
|
||||
/** If this would move outisde the text, returns the input index */
|
||||
size_t moveLine(size_t index, int delta) const;
|
||||
|
||||
/// The character index of the start of the line that character #index is on
|
||||
size_t lineStart(size_t index) const;
|
||||
/// The character index past the end of the line that character #index is on
|
||||
size_t lineEnd (size_t index) const;
|
||||
|
||||
/// Find the index of the character at the given position
|
||||
/** If the position is before everything returns 0,
|
||||
* if it is after everything returns text.size().
|
||||
* The position is in internal coordinates */
|
||||
size_t indexAt(const RealPoint& pos) const;
|
||||
/// Find the position of the character at the given index
|
||||
/** The position is in internal coordinates */
|
||||
RealPoint posOf(size_t index) const;
|
||||
|
||||
/// Return the rectangle around a single character
|
||||
/** If 'first' prefers earlier lines */
|
||||
RealRect charRect(size_t index, bool first) const;
|
||||
/// Is the character at the given index visible?
|
||||
bool isVisible(size_t index) const;
|
||||
/// Find the first character index that is at/before/after the given index, and which has a nonzero width
|
||||
/** More precisely: it returns a position so that the character after it in the direction delta has nonzero width
|
||||
*/
|
||||
size_t firstVisibleChar(size_t index, int delta) const;
|
||||
|
||||
/// Return the height of the last line
|
||||
double heightOfLastLine() const;
|
||||
|
||||
// --------------------------------------------------- : Lines/scrolling
|
||||
|
||||
/// The total number of lines
|
||||
size_t lineCount() const;
|
||||
/// number of fully visible lines, height gives the height of the box
|
||||
size_t visibleLineCount(double height) const;
|
||||
/// the index of the first visible line
|
||||
size_t firstVisibleLine() const;
|
||||
// scroll so line_id becomes the first visible line
|
||||
void scrollTo(size_t line_id);
|
||||
/// Ensure the specified character is fully visible
|
||||
/* Always scrolls by a whole line.
|
||||
* Returns true if the editor has scrolled.
|
||||
*/
|
||||
bool ensureVisible(double height, size_t char_id);
|
||||
|
||||
/// Get exact scroll position
|
||||
double getExactScrollPosition() const;
|
||||
/// Set exact scroll position
|
||||
void setExactScrollPosition(double pos);
|
||||
|
||||
private:
|
||||
/// Scroll all lines a given amount
|
||||
void scrollBy(double delta);
|
||||
|
||||
/// Scroll all lines a given amount
|
||||
void scrollBy(double delta);
|
||||
|
||||
private:
|
||||
// --------------------------------------------------- : More drawing
|
||||
double scale; ///< Scale when drawing
|
||||
|
||||
// --------------------------------------------------- : Elements
|
||||
TextElements elements; ///< The elements of the prepared text
|
||||
|
||||
/// Find the elements in a string and add them to elements
|
||||
void prepareElements(const String&, const TextStyle& style, Context& ctx);
|
||||
|
||||
// --------------------------------------------------- : Lines
|
||||
vector<Line> lines; ///< The lines in the text box
|
||||
|
||||
/// Prepare the lines, layout the text
|
||||
void prepareLines(RotatedDC& dc, const String& text, TextStyle& style, Context& ctx);
|
||||
/// Find the scale to use for the text
|
||||
void prepareLinesTryScales(RotatedDC& dc, const String& text, const TextStyle& style, vector<CharInfo>& chars_out);
|
||||
/// Prepare the lines, layout the text; at a specific scale
|
||||
/** Stores output in lines_out */
|
||||
bool prepareLinesScale(RotatedDC& dc, const vector<CharInfo>& chars, const TextStyle& style, bool stop_if_too_long, vector<Line>& lines_out) const;
|
||||
/// Align the lines within the textbox
|
||||
void alignLines(RotatedDC& dc, const vector<CharInfo>& chars, const TextStyle& style);
|
||||
/// Align the lines of a single paragraph (a set of lines)
|
||||
void alignParagraph(size_t start_line, size_t end_line, const vector<CharInfo>& chars, const TextStyle& style, const RealRect& box);
|
||||
|
||||
/// Find the line the given index is on, returns the first line if the index is not found
|
||||
const Line& findLine(size_t index) const;
|
||||
|
||||
// helper : get the start coordinate of a line, this is 0 unless there is a contour mask
|
||||
double lineLeft (RotatedDC& dc, const TextStyle& style, double y) const;
|
||||
// helper : get the end coordinate of a line, this is width unless there is a contour mask
|
||||
double lineRight(RotatedDC& dc, const TextStyle& style, double y) const;
|
||||
// --------------------------------------------------- : More drawing
|
||||
double scale; ///< Scale when drawing
|
||||
|
||||
// --------------------------------------------------- : Elements
|
||||
TextElements elements; ///< The elements of the prepared text
|
||||
|
||||
/// Find the elements in a string and add them to elements
|
||||
void prepareElements(const String&, const TextStyle& style, Context& ctx);
|
||||
|
||||
// --------------------------------------------------- : Lines
|
||||
vector<Line> lines; ///< The lines in the text box
|
||||
|
||||
/// Prepare the lines, layout the text
|
||||
void prepareLines(RotatedDC& dc, const String& text, TextStyle& style, Context& ctx);
|
||||
/// Find the scale to use for the text
|
||||
void prepareLinesTryScales(RotatedDC& dc, const String& text, const TextStyle& style, vector<CharInfo>& chars_out);
|
||||
/// Prepare the lines, layout the text; at a specific scale
|
||||
/** Stores output in lines_out */
|
||||
bool prepareLinesScale(RotatedDC& dc, const vector<CharInfo>& chars, const TextStyle& style, bool stop_if_too_long, vector<Line>& lines_out) const;
|
||||
/// Align the lines within the textbox
|
||||
void alignLines(RotatedDC& dc, const vector<CharInfo>& chars, const TextStyle& style);
|
||||
/// Align the lines of a single paragraph (a set of lines)
|
||||
void alignParagraph(size_t start_line, size_t end_line, const vector<CharInfo>& chars, const TextStyle& style, const RealRect& box);
|
||||
|
||||
/// Find the line the given index is on, returns the first line if the index is not found
|
||||
const Line& findLine(size_t index) const;
|
||||
|
||||
// helper : get the start coordinate of a line, this is 0 unless there is a contour mask
|
||||
double lineLeft (RotatedDC& dc, const TextStyle& style, double y) const;
|
||||
// helper : get the end coordinate of a line, this is width unless there is a contour mask
|
||||
double lineRight(RotatedDC& dc, const TextStyle& style, double y) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
+88
-88
@@ -17,108 +17,108 @@ IMPLEMENT_VALUE_VIEWER(Choice);
|
||||
void get_options(Rotation& rot, ValueViewer& viewer, const ChoiceStyle& style, GeneratedImage::Options& opts);
|
||||
|
||||
bool ChoiceValueViewer::prepare(RotatedDC& dc) {
|
||||
return prepare_choice_viewer(dc, *this, style(), value().value());
|
||||
return prepare_choice_viewer(dc, *this, style(), value().value());
|
||||
}
|
||||
void ChoiceValueViewer::draw(RotatedDC& dc) {
|
||||
drawFieldBorder(dc);
|
||||
if (style().render_style & RENDER_HIDDEN) return;
|
||||
draw_choice_viewer(dc, *this, style(), value().value());
|
||||
drawFieldBorder(dc);
|
||||
if (style().render_style & RENDER_HIDDEN) return;
|
||||
draw_choice_viewer(dc, *this, style(), value().value());
|
||||
}
|
||||
|
||||
void ChoiceValueViewer::onStyleChange(int changes) {
|
||||
if (changes & CHANGE_MASK) style().image.clearCache();
|
||||
ValueViewer::onStyleChange(changes);
|
||||
if (changes & CHANGE_MASK) style().image.clearCache();
|
||||
ValueViewer::onStyleChange(changes);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Generic draw/prepare
|
||||
|
||||
bool prepare_choice_viewer(RotatedDC& dc, ValueViewer& viewer, ChoiceStyle& style, const String& value) {
|
||||
if (style.render_style & RENDER_IMAGE) {
|
||||
style.initImage();
|
||||
CachedScriptableImage& img = style.image;
|
||||
Context& ctx = viewer.viewer.getContext();
|
||||
ctx.setVariable(SCRIPT_VAR_input, to_script(value));
|
||||
// generate to determine the size
|
||||
if (img.update(ctx) && img.isReady()) {
|
||||
GeneratedImage::Options img_options;
|
||||
get_options(dc, viewer, style, img_options);
|
||||
// Generate image/bitmap (whichever is available)
|
||||
// don't worry, we cache the image
|
||||
ImageCombine combine = style.combine;
|
||||
Bitmap bitmap; Image image;
|
||||
RealSize size;
|
||||
img.generateCached(img_options, &style.mask, &combine, &bitmap, &image, &size);
|
||||
// store content properties
|
||||
if (style.content_width != size.width || style.content_height != size.height) {
|
||||
style.content_width = size.width;
|
||||
style.content_height = size.height;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
if (style.render_style & RENDER_IMAGE) {
|
||||
style.initImage();
|
||||
CachedScriptableImage& img = style.image;
|
||||
Context& ctx = viewer.viewer.getContext();
|
||||
ctx.setVariable(SCRIPT_VAR_input, to_script(value));
|
||||
// generate to determine the size
|
||||
if (img.update(ctx) && img.isReady()) {
|
||||
GeneratedImage::Options img_options;
|
||||
get_options(dc, viewer, style, img_options);
|
||||
// Generate image/bitmap (whichever is available)
|
||||
// don't worry, we cache the image
|
||||
ImageCombine combine = style.combine;
|
||||
Bitmap bitmap; Image image;
|
||||
RealSize size;
|
||||
img.generateCached(img_options, &style.mask, &combine, &bitmap, &image, &size);
|
||||
// store content properties
|
||||
if (style.content_width != size.width || style.content_height != size.height) {
|
||||
style.content_width = size.width;
|
||||
style.content_height = size.height;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void draw_choice_viewer(RotatedDC& dc, ValueViewer& viewer, ChoiceStyle& style, const String& value) {
|
||||
if (value.empty()) return;
|
||||
double margin = 0;
|
||||
if (style.render_style & RENDER_IMAGE) {
|
||||
// draw image
|
||||
CachedScriptableImage& img = style.image;
|
||||
if (style.content_dependent) {
|
||||
// re run script
|
||||
Context& ctx = viewer.viewer.getContext();
|
||||
ctx.setVariable(SCRIPT_VAR_input, to_script(value));
|
||||
img.update(ctx);
|
||||
}
|
||||
if (img.isReady()) {
|
||||
GeneratedImage::Options img_options;
|
||||
get_options(dc, viewer, style, img_options);
|
||||
// Generate image/bitmap
|
||||
ImageCombine combine = style.combine;
|
||||
Bitmap bitmap; Image 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,rect);
|
||||
} else {
|
||||
// use combine mode
|
||||
dc.DrawPreRotatedImage(image,rect,combine);
|
||||
}
|
||||
margin = size.width + 2;
|
||||
} else if (viewer.nativeLook()) {
|
||||
// always have the margin
|
||||
margin = 18;
|
||||
}
|
||||
}
|
||||
if (style.render_style & RENDER_TEXT) {
|
||||
String text = tr(viewer.getStylePackage(), value, capitalize_sentence);
|
||||
Alignment text_align = style.alignment;
|
||||
if (style.render_style & RENDER_IMAGE) {
|
||||
text_align = ALIGN_MIDDLE_LEFT; // can't align both text and image in the same way
|
||||
}
|
||||
dc.SetFont(style.font, 1.0);
|
||||
RealSize size = dc.GetTextExtent(text);
|
||||
RealPoint pos = align_in_rect(text_align, size, dc.getInternalRect()) + RealSize(margin, 0);
|
||||
dc.DrawTextWithShadow(text, style.font, pos);
|
||||
}
|
||||
if (value.empty()) return;
|
||||
double margin = 0;
|
||||
if (style.render_style & RENDER_IMAGE) {
|
||||
// draw image
|
||||
CachedScriptableImage& img = style.image;
|
||||
if (style.content_dependent) {
|
||||
// re run script
|
||||
Context& ctx = viewer.viewer.getContext();
|
||||
ctx.setVariable(SCRIPT_VAR_input, to_script(value));
|
||||
img.update(ctx);
|
||||
}
|
||||
if (img.isReady()) {
|
||||
GeneratedImage::Options img_options;
|
||||
get_options(dc, viewer, style, img_options);
|
||||
// Generate image/bitmap
|
||||
ImageCombine combine = style.combine;
|
||||
Bitmap bitmap; Image 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,rect);
|
||||
} else {
|
||||
// use combine mode
|
||||
dc.DrawPreRotatedImage(image,rect,combine);
|
||||
}
|
||||
margin = size.width + 2;
|
||||
} else if (viewer.nativeLook()) {
|
||||
// always have the margin
|
||||
margin = 18;
|
||||
}
|
||||
}
|
||||
if (style.render_style & RENDER_TEXT) {
|
||||
String text = tr(viewer.getStylePackage(), value, capitalize_sentence);
|
||||
Alignment text_align = style.alignment;
|
||||
if (style.render_style & RENDER_IMAGE) {
|
||||
text_align = ALIGN_MIDDLE_LEFT; // can't align both text and image in the same way
|
||||
}
|
||||
dc.SetFont(style.font, 1.0);
|
||||
RealSize size = dc.GetTextExtent(text);
|
||||
RealPoint pos = align_in_rect(text_align, size, dc.getInternalRect()) + RealSize(margin, 0);
|
||||
dc.DrawTextWithShadow(text, style.font, pos);
|
||||
}
|
||||
}
|
||||
|
||||
void get_options(Rotation& rot, ValueViewer& viewer, const ChoiceStyle& style, GeneratedImage::Options& opts) {
|
||||
opts.package = &viewer.getStylePackage();
|
||||
opts.local_package = &viewer.getLocalPackage();
|
||||
opts.angle = rot.getAngle();
|
||||
if (viewer.nativeLook()) {
|
||||
opts.width = opts.height = 16;
|
||||
opts.preserve_aspect = ASPECT_BORDER;
|
||||
} else if(style.render_style & RENDER_TEXT) {
|
||||
// also drawing text, use original size
|
||||
} else {
|
||||
opts.width = (int) rot.trX(style.width);
|
||||
opts.height = (int) rot.trY(style.height);
|
||||
opts.preserve_aspect = (style.alignment & ALIGN_STRETCH) ? ASPECT_STRETCH : ASPECT_FIT;
|
||||
}
|
||||
opts.package = &viewer.getStylePackage();
|
||||
opts.local_package = &viewer.getLocalPackage();
|
||||
opts.angle = rot.getAngle();
|
||||
if (viewer.nativeLook()) {
|
||||
opts.width = opts.height = 16;
|
||||
opts.preserve_aspect = ASPECT_BORDER;
|
||||
} else if(style.render_style & RENDER_TEXT) {
|
||||
// also drawing text, use original size
|
||||
} else {
|
||||
opts.width = (int) rot.trX(style.width);
|
||||
opts.height = (int) rot.trY(style.height);
|
||||
opts.preserve_aspect = (style.alignment & ALIGN_STRETCH) ? ASPECT_STRETCH : ASPECT_FIT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
/// Viewer that displays a choice value
|
||||
class ChoiceValueViewer : public ValueViewer {
|
||||
public:
|
||||
DECLARE_VALUE_VIEWER(Choice) : ValueViewer(parent,style) {}
|
||||
|
||||
virtual bool prepare(RotatedDC& dc);
|
||||
virtual void draw(RotatedDC& dc);
|
||||
virtual void onStyleChange(int);
|
||||
DECLARE_VALUE_VIEWER(Choice) : ValueViewer(parent,style) {}
|
||||
|
||||
virtual bool prepare(RotatedDC& dc);
|
||||
virtual void draw(RotatedDC& dc);
|
||||
virtual void onStyleChange(int);
|
||||
};
|
||||
|
||||
bool prepare_choice_viewer(RotatedDC& dc, ValueViewer& viewer, ChoiceStyle& style, const String& value);
|
||||
|
||||
+62
-62
@@ -17,69 +17,69 @@ DECLARE_TYPEOF_COLLECTION(ColorField::ChoiceP);
|
||||
IMPLEMENT_VALUE_VIEWER(Color);
|
||||
|
||||
void ColorValueViewer::draw(RotatedDC& dc) {
|
||||
// draw in the value color
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetBrush(value().value());
|
||||
if (nativeLook()) {
|
||||
// native look
|
||||
// find name of color
|
||||
String color_name = _("Custom");
|
||||
if (field().default_script && value().value.isDefault()) {
|
||||
color_name = field().default_name;
|
||||
} else {
|
||||
FOR_EACH_CONST(c, field().choices) {
|
||||
if (value().value() == c->color) {
|
||||
color_name = capitalize(c->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// draw name and color
|
||||
dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
||||
dc.DrawRectangle(RealRect(0, 0, 40, dc.getHeight()));
|
||||
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.DrawRectangle(RealRect(40, 0, dc.getWidth()-40, dc.getHeight()));
|
||||
dc.DrawText(color_name, RealPoint(43, 3));
|
||||
} else {
|
||||
// is there a mask?
|
||||
const AlphaMask& alpha_mask = getMask(dc);
|
||||
if (alpha_mask.isLoaded()) {
|
||||
dc.DrawImage(alpha_mask.colorImage(value().value()), RealPoint(0,0), style().combine);
|
||||
} else {
|
||||
// do we need clipping?
|
||||
bool clip = style().left_width < style().width && style().right_width < style().width &&
|
||||
style().top_width < style().height && style().bottom_width < style().height;
|
||||
if (clip) {
|
||||
// clip away the inside of the rectangle
|
||||
wxRegion r = dc.trRectToRegion(style().getInternalRect());
|
||||
r.Subtract(dc.trRectToRegion(RealRect(
|
||||
style().left_width,
|
||||
style().top_width,
|
||||
style().width - style().left_width - style().right_width,
|
||||
style().height - style().top_width - style().bottom_width
|
||||
)));
|
||||
dc.getDC().SetClippingRegion(r);
|
||||
}
|
||||
dc.DrawRoundedRectangle(style().getInternalRect(), style().radius);
|
||||
if (clip) dc.getDC().DestroyClippingRegion();
|
||||
}
|
||||
drawFieldBorder(dc);
|
||||
}
|
||||
// draw in the value color
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetBrush(value().value());
|
||||
if (nativeLook()) {
|
||||
// native look
|
||||
// find name of color
|
||||
String color_name = _("Custom");
|
||||
if (field().default_script && value().value.isDefault()) {
|
||||
color_name = field().default_name;
|
||||
} else {
|
||||
FOR_EACH_CONST(c, field().choices) {
|
||||
if (value().value() == c->color) {
|
||||
color_name = capitalize(c->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// draw name and color
|
||||
dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
||||
dc.DrawRectangle(RealRect(0, 0, 40, dc.getHeight()));
|
||||
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.DrawRectangle(RealRect(40, 0, dc.getWidth()-40, dc.getHeight()));
|
||||
dc.DrawText(color_name, RealPoint(43, 3));
|
||||
} else {
|
||||
// is there a mask?
|
||||
const AlphaMask& alpha_mask = getMask(dc);
|
||||
if (alpha_mask.isLoaded()) {
|
||||
dc.DrawImage(alpha_mask.colorImage(value().value()), RealPoint(0,0), style().combine);
|
||||
} else {
|
||||
// do we need clipping?
|
||||
bool clip = style().left_width < style().width && style().right_width < style().width &&
|
||||
style().top_width < style().height && style().bottom_width < style().height;
|
||||
if (clip) {
|
||||
// clip away the inside of the rectangle
|
||||
wxRegion r = dc.trRectToRegion(style().getInternalRect());
|
||||
r.Subtract(dc.trRectToRegion(RealRect(
|
||||
style().left_width,
|
||||
style().top_width,
|
||||
style().width - style().left_width - style().right_width,
|
||||
style().height - style().top_width - style().bottom_width
|
||||
)));
|
||||
dc.getDC().SetClippingRegion(r);
|
||||
}
|
||||
dc.DrawRoundedRectangle(style().getInternalRect(), style().radius);
|
||||
if (clip) dc.getDC().DestroyClippingRegion();
|
||||
}
|
||||
drawFieldBorder(dc);
|
||||
}
|
||||
}
|
||||
|
||||
bool ColorValueViewer::containsPoint(const RealPoint& p) const {
|
||||
// check against mask
|
||||
const AlphaMask& alpha_mask = getMask();
|
||||
if (alpha_mask.isLoaded()) {
|
||||
// check against mask
|
||||
return alpha_mask.isOpaque(p, style().getSize());
|
||||
} else {
|
||||
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) return false; // outside bounding box
|
||||
// check against border
|
||||
return left < style().left_width || right < style().right_width // inside horizontal border
|
||||
|| top < style().top_width || bottom < style().bottom_width; // inside vertical border
|
||||
}
|
||||
// check against mask
|
||||
const AlphaMask& alpha_mask = getMask();
|
||||
if (alpha_mask.isLoaded()) {
|
||||
// check against mask
|
||||
return alpha_mask.isOpaque(p, style().getSize());
|
||||
} else {
|
||||
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) return false; // outside bounding box
|
||||
// check against border
|
||||
return left < style().left_width || right < style().right_width // inside horizontal border
|
||||
|| top < style().top_width || bottom < style().bottom_width; // inside vertical border
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,10 @@ DECLARE_POINTER_TYPE(AlphaMask);
|
||||
/// Viewer that displays a color value
|
||||
class ColorValueViewer : public ValueViewer {
|
||||
public:
|
||||
DECLARE_VALUE_VIEWER(Color) : ValueViewer(parent,style) {}
|
||||
|
||||
virtual void draw(RotatedDC& dc);
|
||||
virtual bool containsPoint(const RealPoint& p) const;
|
||||
DECLARE_VALUE_VIEWER(Color) : ValueViewer(parent,style) {}
|
||||
|
||||
virtual void draw(RotatedDC& dc);
|
||||
virtual bool containsPoint(const RealPoint& p) const;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+105
-105
@@ -16,122 +16,122 @@
|
||||
IMPLEMENT_VALUE_VIEWER(Image);
|
||||
|
||||
void ImageValueViewer::draw(RotatedDC& dc) {
|
||||
DrawWhat what = viewer.drawWhat(this);
|
||||
// reset?
|
||||
int w = max(0,(int)dc.trX(style().width)), h = max(0,(int)dc.trY(style().height));
|
||||
Radians a = dc.getAngle();
|
||||
const AlphaMask& alpha_mask = getMask(w,h);
|
||||
if (bitmap.Ok() && (a != angle || size.width != w || size.height != h)) {
|
||||
bitmap = Bitmap();
|
||||
}
|
||||
// try to load image
|
||||
if (!bitmap.Ok()) {
|
||||
angle = a;
|
||||
is_default = false;
|
||||
Image image;
|
||||
// load from file
|
||||
if (!value().filename.empty()) {
|
||||
try {
|
||||
InputStreamP image_file = getLocalPackage().openIn(value().filename);
|
||||
if (image.LoadFile(*image_file)) {
|
||||
image.Rescale(w, h);
|
||||
}
|
||||
} CATCH_ALL_ERRORS(false);
|
||||
}
|
||||
// nice placeholder
|
||||
if (!image.Ok() && style().default_image.isReady()) {
|
||||
image = style().default_image.generate(GeneratedImage::Options(w, h, &getStylePackage(), &getLocalPackage()));
|
||||
is_default = true;
|
||||
if (what & DRAW_EDITING) {
|
||||
bitmap = imagePlaceholder(dc, w, h, image, what & DRAW_EDITING);
|
||||
if (alpha_mask.isLoaded() || !is_rad0(a)) {
|
||||
image = bitmap.ConvertToImage(); // we need to convert back to an image
|
||||
} else {
|
||||
image = Image();
|
||||
}
|
||||
}
|
||||
}
|
||||
// checkerboard placeholder
|
||||
if (!image.Ok() && !bitmap.Ok() && style().width > 40) {
|
||||
// placeholder bitmap
|
||||
bitmap = imagePlaceholder(dc, w, h, wxNullImage, what & DRAW_EDITING);
|
||||
if (alpha_mask.isLoaded() || !is_rad0(a)) {
|
||||
// we need to convert back to an image
|
||||
image = bitmap.ConvertToImage();
|
||||
}
|
||||
}
|
||||
// done
|
||||
if (image.Ok()) {
|
||||
// apply mask and rotate
|
||||
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, dc.getInternalRect());
|
||||
}
|
||||
DrawWhat what = viewer.drawWhat(this);
|
||||
// reset?
|
||||
int w = max(0,(int)dc.trX(style().width)), h = max(0,(int)dc.trY(style().height));
|
||||
Radians a = dc.getAngle();
|
||||
const AlphaMask& alpha_mask = getMask(w,h);
|
||||
if (bitmap.Ok() && (a != angle || size.width != w || size.height != h)) {
|
||||
bitmap = Bitmap();
|
||||
}
|
||||
// try to load image
|
||||
if (!bitmap.Ok()) {
|
||||
angle = a;
|
||||
is_default = false;
|
||||
Image image;
|
||||
// load from file
|
||||
if (!value().filename.empty()) {
|
||||
try {
|
||||
InputStreamP image_file = getLocalPackage().openIn(value().filename);
|
||||
if (image.LoadFile(*image_file)) {
|
||||
image.Rescale(w, h);
|
||||
}
|
||||
} CATCH_ALL_ERRORS(false);
|
||||
}
|
||||
// nice placeholder
|
||||
if (!image.Ok() && style().default_image.isReady()) {
|
||||
image = style().default_image.generate(GeneratedImage::Options(w, h, &getStylePackage(), &getLocalPackage()));
|
||||
is_default = true;
|
||||
if (what & DRAW_EDITING) {
|
||||
bitmap = imagePlaceholder(dc, w, h, image, what & DRAW_EDITING);
|
||||
if (alpha_mask.isLoaded() || !is_rad0(a)) {
|
||||
image = bitmap.ConvertToImage(); // we need to convert back to an image
|
||||
} else {
|
||||
image = Image();
|
||||
}
|
||||
}
|
||||
}
|
||||
// checkerboard placeholder
|
||||
if (!image.Ok() && !bitmap.Ok() && style().width > 40) {
|
||||
// placeholder bitmap
|
||||
bitmap = imagePlaceholder(dc, w, h, wxNullImage, what & DRAW_EDITING);
|
||||
if (alpha_mask.isLoaded() || !is_rad0(a)) {
|
||||
// we need to convert back to an image
|
||||
image = bitmap.ConvertToImage();
|
||||
}
|
||||
}
|
||||
// done
|
||||
if (image.Ok()) {
|
||||
// apply mask and rotate
|
||||
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, dc.getInternalRect());
|
||||
}
|
||||
}
|
||||
|
||||
void ImageValueViewer::onValueChange() {
|
||||
bitmap = Bitmap();
|
||||
bitmap = Bitmap();
|
||||
}
|
||||
|
||||
void ImageValueViewer::onStyleChange(int changes) {
|
||||
if ((changes & CHANGE_MASK) ||
|
||||
((changes & CHANGE_DEFAULT) && is_default)) {
|
||||
bitmap = Bitmap();
|
||||
}
|
||||
ValueViewer::onStyleChange(changes);
|
||||
if ((changes & CHANGE_MASK) ||
|
||||
((changes & CHANGE_DEFAULT) && is_default)) {
|
||||
bitmap = Bitmap();
|
||||
}
|
||||
ValueViewer::onStyleChange(changes);
|
||||
}
|
||||
|
||||
// is an image very light?
|
||||
bool very_light(const Image& image) {
|
||||
int w = image.GetWidth(), h = image.GetHeight();
|
||||
if (w*h<1) return false;
|
||||
Byte* data = image.GetData();
|
||||
int middle = w / 2 + (h*w) / 2;
|
||||
int total = (int)data[3 * middle] + (int)data[3 * middle + 1] + (int)data[3 * middle + 2];
|
||||
return total >= 210 * 3;
|
||||
int w = image.GetWidth(), h = image.GetHeight();
|
||||
if (w*h<1) return false;
|
||||
Byte* data = image.GetData();
|
||||
int middle = w / 2 + (h*w) / 2;
|
||||
int total = (int)data[3 * middle] + (int)data[3 * middle + 1] + (int)data[3 * middle + 2];
|
||||
return total >= 210 * 3;
|
||||
}
|
||||
|
||||
Bitmap ImageValueViewer::imagePlaceholder(const Rotation& rot, UInt w, UInt h, const Image& background, bool editing) {
|
||||
// Bitmap and memory dc
|
||||
Bitmap bmp(w, h, 24);
|
||||
wxMemoryDC mdc;
|
||||
mdc.SelectObject(bmp);
|
||||
RealRect rect(0,0,w,h);
|
||||
RotatedDC dc(mdc, 0, rect, 1.0, QUALITY_AA);
|
||||
// Draw (checker) background
|
||||
if (!background.Ok() || background.HasAlpha()) {
|
||||
draw_checker(dc, rect);
|
||||
}
|
||||
if (background.Ok()) {
|
||||
dc.DrawImage(background, RealPoint(0,0));
|
||||
}
|
||||
// Draw text
|
||||
if (editing) {
|
||||
// only when in editor mode
|
||||
for (UInt size = 12 ; size > 2 ; --size) {
|
||||
dc.SetFont(wxFont(size, wxSWISS, wxNORMAL, wxNORMAL));
|
||||
RealSize rs = dc.GetTextExtent(_("double click to load image"));
|
||||
if (rs.width <= w - 10 && rs.height < h - 10) {
|
||||
// text fits
|
||||
RealPoint pos = align_in_rect(ALIGN_MIDDLE_CENTER, rs, rect);
|
||||
bool black_on_white = !background.Ok() || very_light(background);
|
||||
dc.SetTextForeground(black_on_white ? *wxWHITE : *wxBLACK);
|
||||
dc.DrawText(_("double click to load image"), pos, 2, 4); // blurred
|
||||
dc.SetTextForeground(black_on_white ? *wxBLACK : *wxWHITE);
|
||||
dc.DrawText(_("double click to load image"), pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Done
|
||||
mdc.SelectObject(wxNullBitmap);
|
||||
return bmp;
|
||||
// Bitmap and memory dc
|
||||
Bitmap bmp(w, h, 24);
|
||||
wxMemoryDC mdc;
|
||||
mdc.SelectObject(bmp);
|
||||
RealRect rect(0,0,w,h);
|
||||
RotatedDC dc(mdc, 0, rect, 1.0, QUALITY_AA);
|
||||
// Draw (checker) background
|
||||
if (!background.Ok() || background.HasAlpha()) {
|
||||
draw_checker(dc, rect);
|
||||
}
|
||||
if (background.Ok()) {
|
||||
dc.DrawImage(background, RealPoint(0,0));
|
||||
}
|
||||
// Draw text
|
||||
if (editing) {
|
||||
// only when in editor mode
|
||||
for (UInt size = 12 ; size > 2 ; --size) {
|
||||
dc.SetFont(wxFont(size, wxSWISS, wxNORMAL, wxNORMAL));
|
||||
RealSize rs = dc.GetTextExtent(_("double click to load image"));
|
||||
if (rs.width <= w - 10 && rs.height < h - 10) {
|
||||
// text fits
|
||||
RealPoint pos = align_in_rect(ALIGN_MIDDLE_CENTER, rs, rect);
|
||||
bool black_on_white = !background.Ok() || very_light(background);
|
||||
dc.SetTextForeground(black_on_white ? *wxWHITE : *wxBLACK);
|
||||
dc.DrawText(_("double click to load image"), pos, 2, 4); // blurred
|
||||
dc.SetTextForeground(black_on_white ? *wxBLACK : *wxWHITE);
|
||||
dc.DrawText(_("double click to load image"), pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Done
|
||||
mdc.SelectObject(wxNullBitmap);
|
||||
return bmp;
|
||||
}
|
||||
|
||||
+13
-13
@@ -20,20 +20,20 @@ DECLARE_POINTER_TYPE(AlphaMask);
|
||||
/// Viewer that displays an image value
|
||||
class ImageValueViewer : public ValueViewer {
|
||||
public:
|
||||
DECLARE_VALUE_VIEWER(Image) : ValueViewer(parent,style) {}
|
||||
|
||||
virtual void draw(RotatedDC& dc);
|
||||
virtual void onValueChange();
|
||||
virtual void onStyleChange(int);
|
||||
|
||||
DECLARE_VALUE_VIEWER(Image) : ValueViewer(parent,style) {}
|
||||
|
||||
virtual void draw(RotatedDC& dc);
|
||||
virtual void onValueChange();
|
||||
virtual void onStyleChange(int);
|
||||
|
||||
private:
|
||||
Bitmap bitmap; ///< Cached bitmap
|
||||
RealSize size; ///< Size of cached bitmap
|
||||
Radians angle; ///< Angle of cached bitmap
|
||||
bool is_default; ///< Is the default placeholder image used?
|
||||
|
||||
/// Generate a placeholder image
|
||||
static Bitmap imagePlaceholder(const Rotation& rot, UInt w, UInt h, const Image& background, bool editing);
|
||||
Bitmap bitmap; ///< Cached bitmap
|
||||
RealSize size; ///< Size of cached bitmap
|
||||
Radians angle; ///< Angle of cached bitmap
|
||||
bool is_default; ///< Is the default placeholder image used?
|
||||
|
||||
/// Generate a placeholder image
|
||||
static Bitmap imagePlaceholder(const Rotation& rot, UInt w, UInt h, const Image& background, bool editing);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
@@ -15,26 +15,26 @@
|
||||
IMPLEMENT_VALUE_VIEWER(Info);
|
||||
|
||||
void InfoValueViewer::draw(RotatedDC& dc) {
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
if (nativeLook()) {
|
||||
dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
||||
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_APPWORKSPACE));
|
||||
dc.SetFont(wxFont(16, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, _("Arial")));
|
||||
} else {
|
||||
dc.SetTextForeground(style().font.color);
|
||||
dc.SetBrush(style().background_color);
|
||||
dc.SetFont(style().font, 1.0);
|
||||
}
|
||||
// draw background
|
||||
RealRect rect = style().getInternalRect();
|
||||
dc.DrawRectangle(rect.grow(2));
|
||||
// draw text
|
||||
rect = rect.move(
|
||||
style().padding_left,
|
||||
style().padding_top,
|
||||
-style().padding_left - style().padding_right,
|
||||
-style().padding_top - style().padding_bottom
|
||||
);
|
||||
RealSize size = dc.GetTextExtent(value().value);
|
||||
dc.DrawText(value().value, align_in_rect(style().alignment, size, rect));
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
if (nativeLook()) {
|
||||
dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
||||
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_APPWORKSPACE));
|
||||
dc.SetFont(wxFont(16, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, _("Arial")));
|
||||
} else {
|
||||
dc.SetTextForeground(style().font.color);
|
||||
dc.SetBrush(style().background_color);
|
||||
dc.SetFont(style().font, 1.0);
|
||||
}
|
||||
// draw background
|
||||
RealRect rect = style().getInternalRect();
|
||||
dc.DrawRectangle(rect.grow(2));
|
||||
// draw text
|
||||
rect = rect.move(
|
||||
style().padding_left,
|
||||
style().padding_top,
|
||||
-style().padding_left - style().padding_right,
|
||||
-style().padding_top - style().padding_bottom
|
||||
);
|
||||
RealSize size = dc.GetTextExtent(value().value);
|
||||
dc.DrawText(value().value, align_in_rect(style().alignment, size, rect));
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
/// Viewer that displays a text value
|
||||
class InfoValueViewer : public ValueViewer {
|
||||
public:
|
||||
DECLARE_VALUE_VIEWER(Info) : ValueViewer(parent,style) {}
|
||||
|
||||
virtual void draw(RotatedDC& dc);
|
||||
DECLARE_VALUE_VIEWER(Info) : ValueViewer(parent,style) {}
|
||||
|
||||
virtual void draw(RotatedDC& dc);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -19,72 +19,72 @@ DECLARE_TYPEOF_COLLECTION(String);
|
||||
IMPLEMENT_VALUE_VIEWER(MultipleChoice);
|
||||
|
||||
bool MultipleChoiceValueViewer::prepare(RotatedDC& dc) {
|
||||
if (style().render_style & (RENDER_CHECKLIST | RENDER_LIST)) return false;
|
||||
return prepare_choice_viewer(dc, *this, style(), value().value());
|
||||
if (style().render_style & (RENDER_CHECKLIST | RENDER_LIST)) return false;
|
||||
return prepare_choice_viewer(dc, *this, style(), value().value());
|
||||
}
|
||||
|
||||
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().getInternalRect());
|
||||
// selected choices
|
||||
vector<String> selected;
|
||||
value().get(selected);
|
||||
if (style().render_style & RENDER_CHECKLIST) {
|
||||
// render all choices
|
||||
int end = field().choices->lastId();
|
||||
vector<String>::iterator select_it = selected.begin();
|
||||
for (int i = 0 ; i < end ; ++i) {
|
||||
String choice = field().choices->choiceName(i);
|
||||
bool active = select_it != selected.end() && *select_it == choice;
|
||||
if (active) select_it++;
|
||||
drawChoice(dc, pos, choice, active);
|
||||
}
|
||||
} else if (style().render_style & RENDER_LIST) {
|
||||
// render only selected choices
|
||||
FOR_EACH(choice, selected) {
|
||||
drawChoice(dc, pos, choice);
|
||||
}
|
||||
} else {
|
||||
draw_choice_viewer(dc, *this, style(), value().value());
|
||||
}
|
||||
drawFieldBorder(dc);
|
||||
if (style().render_style & RENDER_HIDDEN) return;
|
||||
RealPoint pos = align_in_rect(style().alignment, RealSize(0,0), style().getInternalRect());
|
||||
// selected choices
|
||||
vector<String> selected;
|
||||
value().get(selected);
|
||||
if (style().render_style & RENDER_CHECKLIST) {
|
||||
// render all choices
|
||||
int end = field().choices->lastId();
|
||||
vector<String>::iterator select_it = selected.begin();
|
||||
for (int i = 0 ; i < end ; ++i) {
|
||||
String choice = field().choices->choiceName(i);
|
||||
bool active = select_it != selected.end() && *select_it == choice;
|
||||
if (active) select_it++;
|
||||
drawChoice(dc, pos, choice, active);
|
||||
}
|
||||
} else if (style().render_style & RENDER_LIST) {
|
||||
// render only selected choices
|
||||
FOR_EACH(choice, selected) {
|
||||
drawChoice(dc, pos, choice);
|
||||
}
|
||||
} else {
|
||||
draw_choice_viewer(dc, *this, style(), value().value());
|
||||
}
|
||||
}
|
||||
|
||||
void MultipleChoiceValueViewer::drawChoice(RotatedDC& dc, RealPoint& pos, const String& choice, bool active) {
|
||||
RealSize size; size.height = item_height;
|
||||
if (style().render_style & RENDER_CHECKLIST) {
|
||||
wxRect rect = dc.trRectToBB(RealRect(pos + RealSize(1,1), RealSize(12,12)));
|
||||
draw_checkbox(nullptr, dc.getDC(), rect, active); // TODO
|
||||
size = add_horizontal(size, RealSize(14,16));
|
||||
}
|
||||
if (style().render_style & RENDER_IMAGE) {
|
||||
map<String,ScriptableImage>::iterator it = style().choice_images.find(canonical_name_form(choice));
|
||||
if (it != style().choice_images.end() && it->second.isReady()) {
|
||||
// TODO: caching
|
||||
GeneratedImage::Options options(0,0, &getStylePackage(), &getLocalPackage());
|
||||
options.zoom = dc.getZoom();
|
||||
options.angle = dc.getAngle();
|
||||
Image image = it->second.generate(options);
|
||||
ImageCombine combine = it->second.combine();
|
||||
// TODO : alignment?
|
||||
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.trInvS(RealSize(options.width,options.height)));
|
||||
}
|
||||
}
|
||||
if (style().render_style & RENDER_TEXT) {
|
||||
// draw text
|
||||
String text = tr(getStylePackage(), choice, capitalize_sentence);
|
||||
dc.SetFont(style().font,1);
|
||||
RealSize text_size = dc.GetTextExtent(text);
|
||||
RealPoint text_pos = align_in_rect(ALIGN_MIDDLE_LEFT, text_size, RealRect(pos.x + size.width + 1, pos.y, 0,size.height));
|
||||
dc.DrawTextWithShadow(text, style().font, text_pos);
|
||||
size = add_horizontal(size, text_size);
|
||||
}
|
||||
// next position
|
||||
pos = move_in_direction(style().direction, pos, size, style().spacing);
|
||||
RealSize size; size.height = item_height;
|
||||
if (style().render_style & RENDER_CHECKLIST) {
|
||||
wxRect rect = dc.trRectToBB(RealRect(pos + RealSize(1,1), RealSize(12,12)));
|
||||
draw_checkbox(nullptr, dc.getDC(), rect, active); // TODO
|
||||
size = add_horizontal(size, RealSize(14,16));
|
||||
}
|
||||
if (style().render_style & RENDER_IMAGE) {
|
||||
map<String,ScriptableImage>::iterator it = style().choice_images.find(canonical_name_form(choice));
|
||||
if (it != style().choice_images.end() && it->second.isReady()) {
|
||||
// TODO: caching
|
||||
GeneratedImage::Options options(0,0, &getStylePackage(), &getLocalPackage());
|
||||
options.zoom = dc.getZoom();
|
||||
options.angle = dc.getAngle();
|
||||
Image image = it->second.generate(options);
|
||||
ImageCombine combine = it->second.combine();
|
||||
// TODO : alignment?
|
||||
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.trInvS(RealSize(options.width,options.height)));
|
||||
}
|
||||
}
|
||||
if (style().render_style & RENDER_TEXT) {
|
||||
// draw text
|
||||
String text = tr(getStylePackage(), choice, capitalize_sentence);
|
||||
dc.SetFont(style().font,1);
|
||||
RealSize text_size = dc.GetTextExtent(text);
|
||||
RealPoint text_pos = align_in_rect(ALIGN_MIDDLE_LEFT, text_size, RealRect(pos.x + size.width + 1, pos.y, 0,size.height));
|
||||
dc.DrawTextWithShadow(text, style().font, text_pos);
|
||||
size = add_horizontal(size, text_size);
|
||||
}
|
||||
// next position
|
||||
pos = move_in_direction(style().direction, pos, size, style().spacing);
|
||||
}
|
||||
|
||||
void MultipleChoiceValueViewer::onStyleChange(int changes) {
|
||||
if (changes & CHANGE_MASK) style().image.clearCache();
|
||||
ValueViewer::onStyleChange(changes);
|
||||
if (changes & CHANGE_MASK) style().image.clearCache();
|
||||
ValueViewer::onStyleChange(changes);
|
||||
}
|
||||
|
||||
@@ -18,15 +18,15 @@
|
||||
/// Viewer that displays a multiple choice value
|
||||
class MultipleChoiceValueViewer : public ValueViewer {
|
||||
public:
|
||||
DECLARE_VALUE_VIEWER(MultipleChoice) : ValueViewer(parent,style), item_height(0) {}
|
||||
|
||||
virtual bool prepare(RotatedDC& dc);
|
||||
virtual void draw(RotatedDC& dc);
|
||||
virtual void onStyleChange(int);
|
||||
DECLARE_VALUE_VIEWER(MultipleChoice) : ValueViewer(parent,style), item_height(0) {}
|
||||
|
||||
virtual bool prepare(RotatedDC& dc);
|
||||
virtual void draw(RotatedDC& dc);
|
||||
virtual void onStyleChange(int);
|
||||
protected:
|
||||
double item_height; ///< Height of a single item, or 0 if non uniform
|
||||
double item_height; ///< Height of a single item, or 0 if non uniform
|
||||
private:
|
||||
void drawChoice(RotatedDC& dc, RealPoint& pos, const String& choice, bool active = true);
|
||||
void drawChoice(RotatedDC& dc, RealPoint& pos, const String& choice, bool active = true);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
@@ -18,53 +18,53 @@ DECLARE_TYPEOF_COLLECTION(PackageChoiceValueViewer::Item);
|
||||
IMPLEMENT_VALUE_VIEWER(PackageChoice);
|
||||
|
||||
struct PackageChoiceValueViewer::ComparePackagePosHint {
|
||||
bool operator () (const PackagedP& a, const PackagedP& b) {
|
||||
// use position_hints to determine order
|
||||
if (a->position_hint < b->position_hint) return true;
|
||||
if (a->position_hint > b->position_hint) return false;
|
||||
// ensure a deterministic order: use the names
|
||||
return a->name() < b->name();
|
||||
}
|
||||
bool operator () (const PackagedP& a, const PackagedP& b) {
|
||||
// use position_hints to determine order
|
||||
if (a->position_hint < b->position_hint) return true;
|
||||
if (a->position_hint > b->position_hint) return false;
|
||||
// ensure a deterministic order: use the names
|
||||
return a->name() < b->name();
|
||||
}
|
||||
};
|
||||
|
||||
void PackageChoiceValueViewer::initItems() {
|
||||
vector<PackagedP> choices;
|
||||
package_manager.findMatching(field().match, choices);
|
||||
sort(choices.begin(), choices.end(), ComparePackagePosHint());
|
||||
FOR_EACH(p, choices) {
|
||||
Item i;
|
||||
i.package_name = p->relativeFilename();
|
||||
i.name = capitalize_sentence(p->short_name);
|
||||
Image image;
|
||||
InputStreamP stream = p->openIconFile();
|
||||
if (stream && image.LoadFile(*stream)) {
|
||||
i.image = Bitmap(resample(image, 16,16));
|
||||
}
|
||||
items.push_back(i);
|
||||
}
|
||||
vector<PackagedP> choices;
|
||||
package_manager.findMatching(field().match, choices);
|
||||
sort(choices.begin(), choices.end(), ComparePackagePosHint());
|
||||
FOR_EACH(p, choices) {
|
||||
Item i;
|
||||
i.package_name = p->relativeFilename();
|
||||
i.name = capitalize_sentence(p->short_name);
|
||||
Image image;
|
||||
InputStreamP stream = p->openIconFile();
|
||||
if (stream && image.LoadFile(*stream)) {
|
||||
i.image = Bitmap(resample(image, 16,16));
|
||||
}
|
||||
items.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
void PackageChoiceValueViewer::draw(RotatedDC& dc) {
|
||||
drawFieldBorder(dc);
|
||||
// find item
|
||||
String text = value().package_name;
|
||||
Bitmap image;
|
||||
if (value().package_name.empty()) {
|
||||
text = field().empty_name;
|
||||
} else {
|
||||
FOR_EACH(i, items) {
|
||||
if (i.package_name == value().package_name) {
|
||||
text = i.name;
|
||||
image = i.image;
|
||||
}
|
||||
}
|
||||
}
|
||||
// draw image
|
||||
if (image.Ok()) {
|
||||
dc.DrawBitmap(image, RealPoint(0,0));
|
||||
}
|
||||
// draw text
|
||||
dc.SetFont(style().font, 1.0);
|
||||
RealPoint pos = align_in_rect(ALIGN_MIDDLE_LEFT, RealSize(0, dc.GetCharHeight()), dc.getInternalRect()) + RealSize(17., 0);
|
||||
dc.DrawTextWithShadow(text, style().font, pos);
|
||||
drawFieldBorder(dc);
|
||||
// find item
|
||||
String text = value().package_name;
|
||||
Bitmap image;
|
||||
if (value().package_name.empty()) {
|
||||
text = field().empty_name;
|
||||
} else {
|
||||
FOR_EACH(i, items) {
|
||||
if (i.package_name == value().package_name) {
|
||||
text = i.name;
|
||||
image = i.image;
|
||||
}
|
||||
}
|
||||
}
|
||||
// draw image
|
||||
if (image.Ok()) {
|
||||
dc.DrawBitmap(image, RealPoint(0,0));
|
||||
}
|
||||
// draw text
|
||||
dc.SetFont(style().font, 1.0);
|
||||
RealPoint pos = align_in_rect(ALIGN_MIDDLE_LEFT, RealSize(0, dc.GetCharHeight()), dc.getInternalRect()) + RealSize(17., 0);
|
||||
dc.DrawTextWithShadow(text, style().font, pos);
|
||||
}
|
||||
|
||||
@@ -18,20 +18,20 @@
|
||||
/// Viewer that displays a package choice value
|
||||
class PackageChoiceValueViewer : public ValueViewer {
|
||||
public:
|
||||
DECLARE_VALUE_VIEWER(PackageChoice) : ValueViewer(parent,style) { initItems(); }
|
||||
|
||||
virtual void draw(RotatedDC& dc);
|
||||
|
||||
struct Item{
|
||||
String package_name;
|
||||
String name;
|
||||
Bitmap image;
|
||||
};
|
||||
DECLARE_VALUE_VIEWER(PackageChoice) : ValueViewer(parent,style) { initItems(); }
|
||||
|
||||
virtual void draw(RotatedDC& dc);
|
||||
|
||||
struct Item{
|
||||
String package_name;
|
||||
String name;
|
||||
Bitmap image;
|
||||
};
|
||||
protected:
|
||||
vector<Item> items;
|
||||
vector<Item> items;
|
||||
private:
|
||||
void initItems();
|
||||
struct ComparePackagePosHint;
|
||||
void initItems();
|
||||
struct ComparePackagePosHint;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+31
-31
@@ -21,38 +21,38 @@ DECLARE_TYPEOF_COLLECTION(SymbolVariationP);
|
||||
IMPLEMENT_VALUE_VIEWER(Symbol);
|
||||
|
||||
void SymbolValueViewer::draw(RotatedDC& dc) {
|
||||
drawFieldBorder(dc);
|
||||
// draw checker background
|
||||
draw_checker(dc, style().getInternalRect());
|
||||
double wh = min(dc.getWidth(), dc.getHeight());
|
||||
// try to load symbol
|
||||
if (symbols.empty() && !value().filename.empty()) {
|
||||
try {
|
||||
// load symbol
|
||||
SymbolP symbol = getLocalPackage().readFile<SymbolP>(value().filename);
|
||||
// aspect ratio
|
||||
double ar = symbol->aspectRatio();
|
||||
ar = min(style().max_aspect_ratio, max(style().min_aspect_ratio, ar));
|
||||
// render and filter variations
|
||||
FOR_EACH(variation, style().variations) {
|
||||
Image img = render_symbol(symbol, *variation->filter, variation->border_radius, int(200 * ar), 200);
|
||||
Image resampled(int(wh * ar), int(wh), false);
|
||||
resample(img, resampled);
|
||||
symbols.push_back(Bitmap(resampled));
|
||||
}
|
||||
} catch (const Error& e) {
|
||||
handle_error(e);
|
||||
}
|
||||
}
|
||||
// draw image, if any
|
||||
int x = 0;
|
||||
for (size_t i = 0 ; i < symbols.size() ; ++i) {
|
||||
// todo : labels?
|
||||
dc.DrawBitmap(symbols[i], RealPoint(x, 0));
|
||||
x += symbols[i].GetWidth() + 2;
|
||||
}
|
||||
drawFieldBorder(dc);
|
||||
// draw checker background
|
||||
draw_checker(dc, style().getInternalRect());
|
||||
double wh = min(dc.getWidth(), dc.getHeight());
|
||||
// try to load symbol
|
||||
if (symbols.empty() && !value().filename.empty()) {
|
||||
try {
|
||||
// load symbol
|
||||
SymbolP symbol = getLocalPackage().readFile<SymbolP>(value().filename);
|
||||
// aspect ratio
|
||||
double ar = symbol->aspectRatio();
|
||||
ar = min(style().max_aspect_ratio, max(style().min_aspect_ratio, ar));
|
||||
// render and filter variations
|
||||
FOR_EACH(variation, style().variations) {
|
||||
Image img = render_symbol(symbol, *variation->filter, variation->border_radius, int(200 * ar), 200);
|
||||
Image resampled(int(wh * ar), int(wh), false);
|
||||
resample(img, resampled);
|
||||
symbols.push_back(Bitmap(resampled));
|
||||
}
|
||||
} catch (const Error& e) {
|
||||
handle_error(e);
|
||||
}
|
||||
}
|
||||
// draw image, if any
|
||||
int x = 0;
|
||||
for (size_t i = 0 ; i < symbols.size() ; ++i) {
|
||||
// todo : labels?
|
||||
dc.DrawBitmap(symbols[i], RealPoint(x, 0));
|
||||
x += symbols[i].GetWidth() + 2;
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolValueViewer::onValueChange() {
|
||||
symbols.clear();
|
||||
symbols.clear();
|
||||
}
|
||||
|
||||
@@ -18,13 +18,13 @@
|
||||
/// Viewer that displays a symbol value
|
||||
class SymbolValueViewer : public ValueViewer {
|
||||
public:
|
||||
DECLARE_VALUE_VIEWER(Symbol) : ValueViewer(parent,style) {}
|
||||
|
||||
virtual void draw(RotatedDC& dc);
|
||||
void onValueChange();
|
||||
|
||||
DECLARE_VALUE_VIEWER(Symbol) : ValueViewer(parent,style) {}
|
||||
|
||||
virtual void draw(RotatedDC& dc);
|
||||
void onValueChange();
|
||||
|
||||
protected:
|
||||
vector<Bitmap> symbols; ///< Cached images
|
||||
vector<Bitmap> symbols; ///< Cached images
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+16
-16
@@ -15,35 +15,35 @@
|
||||
IMPLEMENT_VALUE_VIEWER(Text);
|
||||
|
||||
bool TextValueViewer::prepare(RotatedDC& dc) {
|
||||
getMask(dc); // ensure alpha/contour mask is loaded
|
||||
return v.prepare(dc, value().value(), style(), viewer.getContext());
|
||||
getMask(dc); // ensure alpha/contour mask is loaded
|
||||
return v.prepare(dc, value().value(), style(), viewer.getContext());
|
||||
}
|
||||
|
||||
void TextValueViewer::draw(RotatedDC& dc) {
|
||||
drawFieldBorder(dc);
|
||||
if (!v.prepared()) {
|
||||
v.prepare(dc, value().value(), style(), viewer.getContext());
|
||||
dc.setStretch(getStretch());
|
||||
}
|
||||
DrawWhat what = viewer.drawWhat(this);
|
||||
v.draw(dc, style(), (DrawWhat)(what & DRAW_ACTIVE));
|
||||
setFieldBorderPen(dc);
|
||||
v.draw(dc, style(), (DrawWhat)(what & ~DRAW_ACTIVE));
|
||||
drawFieldBorder(dc);
|
||||
if (!v.prepared()) {
|
||||
v.prepare(dc, value().value(), style(), viewer.getContext());
|
||||
dc.setStretch(getStretch());
|
||||
}
|
||||
DrawWhat what = viewer.drawWhat(this);
|
||||
v.draw(dc, style(), (DrawWhat)(what & DRAW_ACTIVE));
|
||||
setFieldBorderPen(dc);
|
||||
v.draw(dc, style(), (DrawWhat)(what & ~DRAW_ACTIVE));
|
||||
}
|
||||
|
||||
void TextValueViewer::onValueChange() {
|
||||
v.reset(false);
|
||||
v.reset(false);
|
||||
}
|
||||
|
||||
void TextValueViewer::onStyleChange(int changes) {
|
||||
v.reset(true);
|
||||
ValueViewer::onStyleChange(changes);
|
||||
v.reset(true);
|
||||
ValueViewer::onStyleChange(changes);
|
||||
}
|
||||
|
||||
void TextValueViewer::onAction(const Action&, bool undone) {
|
||||
v.reset(true);
|
||||
v.reset(true);
|
||||
}
|
||||
|
||||
double TextValueViewer::getStretch() const {
|
||||
return v.prepared() ? style().getStretch() : 1.0;
|
||||
return v.prepared() ? style().getStretch() : 1.0;
|
||||
}
|
||||
|
||||
+10
-10
@@ -19,17 +19,17 @@
|
||||
/// Viewer that displays a text value
|
||||
class TextValueViewer : public ValueViewer {
|
||||
public:
|
||||
DECLARE_VALUE_VIEWER(Text) : ValueViewer(parent,style) {}
|
||||
|
||||
virtual bool prepare(RotatedDC& dc);
|
||||
virtual void draw(RotatedDC& dc);
|
||||
virtual void onValueChange();
|
||||
virtual void onStyleChange(int);
|
||||
virtual void onAction(const Action&, bool undone);
|
||||
virtual double getStretch() const;
|
||||
|
||||
DECLARE_VALUE_VIEWER(Text) : ValueViewer(parent,style) {}
|
||||
|
||||
virtual bool prepare(RotatedDC& dc);
|
||||
virtual void draw(RotatedDC& dc);
|
||||
virtual void onValueChange();
|
||||
virtual void onStyleChange(int);
|
||||
virtual void onAction(const Action&, bool undone);
|
||||
virtual double getStretch() const;
|
||||
|
||||
protected:
|
||||
TextViewer v;
|
||||
TextViewer v;
|
||||
};
|
||||
|
||||
|
||||
|
||||
+40
-40
@@ -15,81 +15,81 @@ DECLARE_TYPEOF_COLLECTION(wxPoint);
|
||||
// ----------------------------------------------------------------------------- : ValueViewer
|
||||
|
||||
ValueViewer::ValueViewer(DataViewer& parent, const StyleP& style)
|
||||
: StyleListener(style), viewer(parent)
|
||||
: StyleListener(style), viewer(parent)
|
||||
{}
|
||||
|
||||
Package& ValueViewer::getStylePackage() const { return viewer.getStylePackage(); }
|
||||
Package& ValueViewer::getLocalPackage() const { return viewer.getLocalPackage(); }
|
||||
|
||||
void ValueViewer::setValue(const ValueP& value) {
|
||||
assert(value->fieldP == styleP->fieldP); // matching field
|
||||
if (valueP == value) return;
|
||||
valueP = value;
|
||||
onValueChange();
|
||||
assert(value->fieldP == styleP->fieldP); // matching field
|
||||
if (valueP == value) return;
|
||||
valueP = value;
|
||||
onValueChange();
|
||||
}
|
||||
|
||||
bool ValueViewer::containsPoint(const RealPoint& p) const {
|
||||
return getMask().isOpaque(p, styleP->getSize());
|
||||
return getMask().isOpaque(p, styleP->getSize());
|
||||
}
|
||||
RealRect ValueViewer::boundingBox() const {
|
||||
return styleP->getExternalRect().grow(1);
|
||||
return styleP->getExternalRect().grow(1);
|
||||
}
|
||||
|
||||
Rotation ValueViewer::getRotation() const {
|
||||
return Rotation(deg_to_rad(getStyle()->angle), getStyle()->getExternalRect(), 1.0, getStretch());
|
||||
return Rotation(deg_to_rad(getStyle()->angle), getStyle()->getExternalRect(), 1.0, getStretch());
|
||||
}
|
||||
|
||||
bool ValueViewer::setFieldBorderPen(RotatedDC& dc) {
|
||||
if (!getField()->editable) return false;
|
||||
DrawWhat what = viewer.drawWhat(this);
|
||||
if (!(what & DRAW_BORDERS)) return false;
|
||||
dc.SetPen( (what & DRAW_ACTIVE)
|
||||
? wxPen(Color(0,128,255), 1, wxSOLID)
|
||||
: wxPen(Color(128,128,128), 1, wxDOT)
|
||||
);
|
||||
return true;
|
||||
if (!getField()->editable) return false;
|
||||
DrawWhat what = viewer.drawWhat(this);
|
||||
if (!(what & DRAW_BORDERS)) return false;
|
||||
dc.SetPen( (what & DRAW_ACTIVE)
|
||||
? wxPen(Color(0,128,255), 1, wxSOLID)
|
||||
: wxPen(Color(128,128,128), 1, wxDOT)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ValueViewer::drawFieldBorder(RotatedDC& dc) {
|
||||
if (setFieldBorderPen(dc)) {
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
const AlphaMask& alpha_mask = getMask(dc);
|
||||
if (alpha_mask.isLoaded()) {
|
||||
// from mask
|
||||
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]);
|
||||
} else {
|
||||
// simple rectangle
|
||||
dc.DrawRectangle(dc.getInternalRect().grow(dc.trInvS(1)));
|
||||
}
|
||||
}
|
||||
if (setFieldBorderPen(dc)) {
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
const AlphaMask& alpha_mask = getMask(dc);
|
||||
if (alpha_mask.isLoaded()) {
|
||||
// from mask
|
||||
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]);
|
||||
} else {
|
||||
// simple rectangle
|
||||
dc.DrawRectangle(dc.getInternalRect().grow(dc.trInvS(1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const AlphaMask& ValueViewer::getMask(int w, int h) const {
|
||||
GeneratedImage::Options opts(w, h, &getStylePackage(), &getLocalPackage());
|
||||
return styleP->mask.get(opts);
|
||||
GeneratedImage::Options opts(w, h, &getStylePackage(), &getLocalPackage());
|
||||
return styleP->mask.get(opts);
|
||||
}
|
||||
const AlphaMask& ValueViewer::getMask(const Rotation& rot) const {
|
||||
return getMask((int)rot.trX(styleP->width), (int)rot.trY(styleP->height));
|
||||
return getMask((int)rot.trX(styleP->width), (int)rot.trY(styleP->height));
|
||||
}
|
||||
|
||||
|
||||
void ValueViewer::redraw() {
|
||||
viewer.redraw(*this);
|
||||
viewer.redraw(*this);
|
||||
}
|
||||
|
||||
bool ValueViewer::nativeLook() const {
|
||||
return viewer.nativeLook();
|
||||
return viewer.nativeLook();
|
||||
}
|
||||
bool ValueViewer::isCurrent() const {
|
||||
return viewer.viewerIsCurrent(this);
|
||||
return viewer.viewerIsCurrent(this);
|
||||
}
|
||||
|
||||
void ValueViewer::onStyleChange(int changes) {
|
||||
if (!(changes & CHANGE_ALREADY_PREPARED)) {
|
||||
viewer.redraw(*this);
|
||||
}
|
||||
if (!(changes & CHANGE_ALREADY_PREPARED)) {
|
||||
viewer.redraw(*this);
|
||||
}
|
||||
}
|
||||
|
||||
+74
-74
@@ -27,77 +27,77 @@ DECLARE_POINTER_TYPE(Value);
|
||||
/** A viewer can only display a value, not edit it, ValueEditor is used for that */
|
||||
class ValueViewer : public StyleListener {
|
||||
public:
|
||||
/// Construct a ValueViewer, set the value at a later time
|
||||
ValueViewer(DataViewer& parent, const StyleP& style);
|
||||
virtual ~ValueViewer() {}
|
||||
|
||||
/// Change the associated value
|
||||
void setValue(const ValueP&);
|
||||
/// Return the associated field
|
||||
inline const FieldP& getField() const { return styleP->fieldP; }
|
||||
/// Return the associated style
|
||||
inline const StyleP& getStyle() const { return styleP; }
|
||||
/// Return the associated value
|
||||
inline const ValueP& getValue() const { return valueP; }
|
||||
|
||||
/// Prepare before drawing.
|
||||
/** Should return true if a content property has changed
|
||||
* Scripts are re-updated after preparing if they depend on content properties. */
|
||||
virtual bool prepare(RotatedDC& dc) { return false; };
|
||||
/// Draw this value
|
||||
virtual void draw(RotatedDC& dc) = 0;
|
||||
|
||||
/// Does this field contian the given point?
|
||||
virtual bool containsPoint(const RealPoint& p) const;
|
||||
/// 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)
|
||||
*/
|
||||
virtual void onValueChange() {}
|
||||
/// Called when a (scripted) property of the associated style has changed
|
||||
/** Default: redraws the viewer if needed */
|
||||
virtual void onStyleChange(int changes);
|
||||
/// Called when an action is performed on the associated value
|
||||
virtual void onAction(const Action&, bool undone) { onValueChange(); }
|
||||
|
||||
/// Convert this viewer to an editor, if possible
|
||||
virtual ValueEditor* getEditor() { return 0; }
|
||||
|
||||
DataViewer& viewer; ///< Our parent object
|
||||
/// Construct a ValueViewer, set the value at a later time
|
||||
ValueViewer(DataViewer& parent, const StyleP& style);
|
||||
virtual ~ValueViewer() {}
|
||||
|
||||
/// Change the associated value
|
||||
void setValue(const ValueP&);
|
||||
/// Return the associated field
|
||||
inline const FieldP& getField() const { return styleP->fieldP; }
|
||||
/// Return the associated style
|
||||
inline const StyleP& getStyle() const { return styleP; }
|
||||
/// Return the associated value
|
||||
inline const ValueP& getValue() const { return valueP; }
|
||||
|
||||
/// Prepare before drawing.
|
||||
/** Should return true if a content property has changed
|
||||
* Scripts are re-updated after preparing if they depend on content properties. */
|
||||
virtual bool prepare(RotatedDC& dc) { return false; };
|
||||
/// Draw this value
|
||||
virtual void draw(RotatedDC& dc) = 0;
|
||||
|
||||
/// Does this field contian the given point?
|
||||
virtual bool containsPoint(const RealPoint& p) const;
|
||||
/// 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)
|
||||
*/
|
||||
virtual void onValueChange() {}
|
||||
/// Called when a (scripted) property of the associated style has changed
|
||||
/** Default: redraws the viewer if needed */
|
||||
virtual void onStyleChange(int changes);
|
||||
/// Called when an action is performed on the associated value
|
||||
virtual void onAction(const Action&, bool undone) { onValueChange(); }
|
||||
|
||||
/// Convert this viewer to an editor, if possible
|
||||
virtual ValueEditor* getEditor() { return 0; }
|
||||
|
||||
DataViewer& viewer; ///< Our parent object
|
||||
protected:
|
||||
ValueP valueP; ///< The value we are currently viewing
|
||||
|
||||
/// Set the pen for drawing the border, returns true if a border needs to be drawn
|
||||
bool setFieldBorderPen(RotatedDC& dc);
|
||||
/// Draws a border around the field
|
||||
void drawFieldBorder(RotatedDC& dc);
|
||||
|
||||
/// Redraw this viewer
|
||||
void redraw();
|
||||
|
||||
/// Load the AlphaMask for this field, scaled but not rotated
|
||||
const AlphaMask& getMask(int w = 0, int h = 0) const;
|
||||
const AlphaMask& getMask(const Rotation& rot) const;
|
||||
|
||||
ValueP valueP; ///< The value we are currently viewing
|
||||
|
||||
/// Set the pen for drawing the border, returns true if a border needs to be drawn
|
||||
bool setFieldBorderPen(RotatedDC& dc);
|
||||
/// Draws a border around the field
|
||||
void drawFieldBorder(RotatedDC& dc);
|
||||
|
||||
/// Redraw this viewer
|
||||
void redraw();
|
||||
|
||||
/// Load the AlphaMask for this field, scaled but not rotated
|
||||
const AlphaMask& getMask(int w = 0, int h = 0) const;
|
||||
const AlphaMask& getMask(const Rotation& rot) const;
|
||||
|
||||
public:
|
||||
/// Should this viewer render using a platform native look?
|
||||
bool nativeLook() const;
|
||||
/// Is this the currently selected viewer?
|
||||
/** Usually only the editor allows selection of viewers */
|
||||
bool isCurrent() const;
|
||||
|
||||
/// The package containing style stuff like images
|
||||
Package& getStylePackage() const;
|
||||
/// The local package for loading/saving files
|
||||
Package& getLocalPackage() const;
|
||||
/// Should this viewer render using a platform native look?
|
||||
bool nativeLook() const;
|
||||
/// Is this the currently selected viewer?
|
||||
/** Usually only the editor allows selection of viewers */
|
||||
bool isCurrent() const;
|
||||
|
||||
/// The package containing style stuff like images
|
||||
Package& getStylePackage() const;
|
||||
/// The local package for loading/saving files
|
||||
Package& getLocalPackage() const;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Utility
|
||||
@@ -113,11 +113,11 @@ class ValueViewer : public StyleListener {
|
||||
public: \
|
||||
Type##ValueViewer(DataViewer& parent, const Type ## StyleP& style)
|
||||
|
||||
#define IMPLEMENT_VALUE_VIEWER(Type) \
|
||||
ValueViewerP Type##Style::makeViewer(DataViewer& parent, const StyleP& thisP) { \
|
||||
assert(thisP.get() == this); \
|
||||
return ValueViewerP(new Type##ValueViewer(parent, static_pointer_cast<Type##Style>(thisP))); \
|
||||
}
|
||||
#define IMPLEMENT_VALUE_VIEWER(Type) \
|
||||
ValueViewerP Type##Style::makeViewer(DataViewer& parent, const StyleP& thisP) { \
|
||||
assert(thisP.get() == this); \
|
||||
return ValueViewerP(new Type##ValueViewer(parent, static_pointer_cast<Type##Style>(thisP))); \
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
Reference in New Issue
Block a user