diff options
author | Marius Kintel <marius@kintel.net> | 2014-01-03 18:17:58 (GMT) |
---|---|---|
committer | Marius Kintel <marius@kintel.net> | 2014-01-03 18:17:58 (GMT) |
commit | bee5233a916055d41bb4ee425f5df25b80f50f16 (patch) | |
tree | 7bfac9716f778ed685e62e2d0830d1905bc08fd2 /src | |
parent | f093b53c3edb08ee0d64c5d6c2a1df723acfca2d (diff) | |
parent | c5223417e3ffe965d09d971865797206080eb0ae (diff) |
Merge branch 'vector-concat' of git://github.com/t-paul/openscad into t-paul-vector-concat
Conflicts:
src/Preferences.ui
Diffstat (limited to 'src')
-rw-r--r-- | src/Preferences.cc | 115 | ||||
-rw-r--r-- | src/Preferences.h | 5 | ||||
-rw-r--r-- | src/Preferences.ui | 92 | ||||
-rw-r--r-- | src/feature.cc | 79 | ||||
-rw-r--r-- | src/feature.h | 44 | ||||
-rw-r--r-- | src/func.cc | 19 | ||||
-rw-r--r-- | src/function.h | 9 | ||||
-rw-r--r-- | src/modcontext.cc | 11 | ||||
-rw-r--r-- | src/module.h | 7 | ||||
-rw-r--r-- | src/openscad.cc | 10 |
10 files changed, 373 insertions, 18 deletions
diff --git a/src/Preferences.cc b/src/Preferences.cc index eed877d..b63ca7b 100644 --- a/src/Preferences.cc +++ b/src/Preferences.cc @@ -33,12 +33,16 @@ #include <QStatusBar> #include "PolySetCache.h" #include "AutoUpdater.h" +#include "feature.h" #ifdef ENABLE_CGAL #include "CGALCache.h" #endif Preferences *Preferences::instance = NULL; +const char * Preferences::featurePropertyName = "FeatureProperty"; +Q_DECLARE_METATYPE(Feature *); + Preferences::Preferences(QWidget *parent) : QMainWindow(parent) { setupUi(this); @@ -89,10 +93,11 @@ Preferences::Preferences(QWidget *parent) : QMainWindow(parent) // Toolbar QActionGroup *group = new QActionGroup(this); - group->addAction(prefsAction3DView); - group->addAction(prefsActionEditor); - group->addAction(prefsActionUpdate); - group->addAction(prefsActionAdvanced); + addPrefPage(group, prefsAction3DView, page3DView); + addPrefPage(group, prefsActionEditor, pageEditor); + addPrefPage(group, prefsActionUpdate, pageUpdate); + addPrefPage(group, prefsActionFeatures, pageFeatures); + addPrefPage(group, prefsActionAdvanced, pageAdvanced); connect(group, SIGNAL(triggered(QAction*)), this, SLOT(actionTriggered(QAction*))); prefsAction3DView->setChecked(true); @@ -140,6 +145,7 @@ Preferences::Preferences(QWidget *parent) : QMainWindow(parent) this->polysetCacheSizeEdit->setValidator(validator); this->opencsgLimitEdit->setValidator(validator); + setupFeaturesPage(); updateGUI(); RenderSettings::inst()->setColors(this->colorschemes[getValue("3dview/colorscheme").toString()]); @@ -150,21 +156,103 @@ Preferences::~Preferences() removeDefaultSettings(); } +/** + * Add a page for the preferences GUI. This handles both the action grouping + * and the registration of the widget for each action to have a generalized + * callback to switch pages. + * + * @param group The action group for all page actions. This one will have the + * callback attached after creating all actions/pages. + * @param action The action specific for the added page. + * @param widget The widget that should be shown when the action is triggered. + * This must be a child page of the stackedWidget. + */ +void +Preferences::addPrefPage(QActionGroup *group, QAction *action, QWidget *widget) +{ + group->addAction(action); + prefPages[action] = widget; +} + +/** + * Callback to switch pages in the preferences GUI. + * + * @param action The action triggered by the user. + */ void Preferences::actionTriggered(QAction *action) { - if (action == this->prefsAction3DView) { - this->stackedWidget->setCurrentWidget(this->page3DView); - } - else if (action == this->prefsActionEditor) { - this->stackedWidget->setCurrentWidget(this->pageEditor); + this->stackedWidget->setCurrentWidget(prefPages[action]); +} + +/** + * Callback for the dynamically created checkboxes on the features + * page. The specific Feature object is associated as property with + * the callback. + * + * @param state the state of the checkbox. + */ +void Preferences::featuresCheckBoxToggled(bool state) +{ + const QObject *sender = QObject::sender(); + if (sender == NULL) { + return; } - else if (action == this->prefsActionUpdate) { - this->stackedWidget->setCurrentWidget(this->pageUpdate); + QVariant v = sender->property(featurePropertyName); + if (!v.isValid()) { + return; } - else if (action == this->prefsActionAdvanced) { - this->stackedWidget->setCurrentWidget(this->pageAdvanced); + Feature *feature = v.value<Feature *>(); + feature->enable(state); + QSettings settings; + settings.setValue(QString("feature/%1").arg(QString::fromStdString(feature->get_name())), state); +} + +/** + * Setup feature GUI and synchronize the Qt settings with the feature values. + * + * In case a feature was enabled on the commandline this will have precedence + * and cause the checkbox in the settings GUI to be not editable. + * Otherwise the value from the Qt settings is pushed into the feature state + * and the checkbox is initialized accordingly. + */ +void +Preferences::setupFeaturesPage() +{ + int row = 0; + for (Feature::iterator it = Feature::begin();it != Feature::end();it++) { + Feature *feature = *it; + + QString featurekey = QString("feature/%1").arg(QString::fromStdString(feature->get_name())); + this->defaultmap[featurekey] = false; + + // spacer item between the features, just for some optical separation + gridLayoutExperimentalFeatures->addItem(new QSpacerItem(1, 8, QSizePolicy::Expanding, QSizePolicy::Fixed), row, 1, 1, 1, Qt::AlignCenter); + row++; + + QCheckBox *cb = new QCheckBox(QString::fromStdString(feature->get_name()), pageFeatures); + QFont bold_font(cb->font()); + bold_font.setBold(true); + cb->setFont(bold_font); + // synchronize Qt settings with the feature settings + bool value = getValue(featurekey).toBool(); + feature->enable(value); + cb->setChecked(value); + cb->setProperty(featurePropertyName, QVariant::fromValue<Feature *>(feature)); + connect(cb, SIGNAL(toggled(bool)), this, SLOT(featuresCheckBoxToggled(bool))); + gridLayoutExperimentalFeatures->addWidget(cb, row, 0, 1, 2, Qt::AlignLeading); + row++; + + QLabel *l = new QLabel(QString::fromStdString(feature->get_description()), pageFeatures); + l->setTextFormat(Qt::RichText); + gridLayoutExperimentalFeatures->addWidget(l, row, 1, 1, 1, Qt::AlignLeading); + row++; } + // Force fixed indentation, the checkboxes use column span of 2 so + // first row is not constrained in size by the visible controls. The + // fixed size space essentially gives the first row the width of the + // spacer item itself. + gridLayoutExperimentalFeatures->addItem(new QSpacerItem(20, 0, QSizePolicy::Fixed, QSizePolicy::Fixed), 1, 0, 1, 1, Qt::AlignLeading); } void Preferences::on_colorSchemeChooser_itemSelectionChanged() @@ -316,7 +404,6 @@ QVariant Preferences::getValue(const QString &key) const void Preferences::updateGUI() { - QSettings settings; QList<QListWidgetItem *> found = this->colorSchemeChooser->findItems(getValue("3dview/colorscheme").toString(), Qt::MatchExactly); diff --git a/src/Preferences.h b/src/Preferences.h index 1230c8a..d74ada9 100644 --- a/src/Preferences.h +++ b/src/Preferences.h @@ -21,6 +21,7 @@ public: public slots: void actionTriggered(class QAction *); + void featuresCheckBoxToggled(bool); void on_colorSchemeChooser_itemSelectionChanged(); void on_fontChooser_activated(const QString &); void on_fontSize_editTextChanged(const QString &); @@ -46,11 +47,15 @@ private: void keyPressEvent(QKeyEvent *e); void updateGUI(); void removeDefaultSettings(); + void setupFeaturesPage(); + void addPrefPage(QActionGroup *group, QAction *action, QWidget *widget); QSettings::SettingsMap defaultmap; QHash<QString, std::map<RenderSettings::RenderColor, Color4f> > colorschemes; + QHash<const QAction *, QWidget *> prefPages; static Preferences *instance; + static const char *featurePropertyName; }; #endif diff --git a/src/Preferences.ui b/src/Preferences.ui index ea039f2..100bcb1 100644 --- a/src/Preferences.ui +++ b/src/Preferences.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>473</width> - <height>320</height> + <width>823</width> + <height>433</height> </rect> </property> <property name="sizePolicy"> @@ -378,6 +378,78 @@ </item> </layout> </widget> + <widget class="QWidget" name="pageFeatures"> + <layout class="QGridLayout" name="gridLayout_2"> + <property name="margin"> + <number>0</number> + </property> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_9"> + <item> + <widget class="QLabel" name="label_9"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Features</string> + </property> + </widget> + </item> + <item> + <widget class="QScrollArea" name="scrollArea"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="horizontalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> + </property> + <property name="widgetResizable"> + <bool>true</bool> + </property> + <widget class="QWidget" name="scrollAreaWidgetContents"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>803</width> + <height>311</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout_10"> + <item> + <layout class="QGridLayout" name="gridLayoutExperimentalFeatures" rowminimumheight="0"> + <property name="spacing"> + <number>8</number> + </property> + </layout> + </item> + <item> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>282</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </item> + </layout> + </widget> <widget class="QWidget" name="pageAdvanced"> <layout class="QVBoxLayout" name="verticalLayout_3"> <item> @@ -520,6 +592,7 @@ <addaction name="prefsAction3DView"/> <addaction name="prefsActionEditor"/> <addaction name="prefsActionUpdate"/> + <addaction name="prefsActionFeatures"/> <addaction name="prefsActionAdvanced"/> </widget> <action name="prefsAction3DView"> @@ -570,6 +643,21 @@ <string>Update</string> </property> </action> + <action name="prefsActionFeatures"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="icon"> + <iconset resource="../openscad.qrc"> + <normaloff>:/icons/prefsFeatures.png</normaloff>:/icons/prefsFeatures.png</iconset> + </property> + <property name="text"> + <string>Features</string> + </property> + <property name="toolTip"> + <string>Enable/Disable experimental features</string> + </property> + </action> </widget> <resources> <include location="../openscad.qrc"/> diff --git a/src/feature.cc b/src/feature.cc new file mode 100644 index 0000000..80d7887 --- /dev/null +++ b/src/feature.cc @@ -0,0 +1,79 @@ +#include <stdio.h> +#include <iostream> +#include <string> +#include <map> + +#include "feature.h" +#include "printutils.h" + +/** + * Feature registration map/list for later lookup. This must be initialized + * before the static feature instances as those register with this map. + */ +Feature::map_t Feature::feature_map; +Feature::list_t Feature::feature_list; + +/* + * List of features, the names given here are used in both command line + * argument to enable the option and for saving the option value in GUI + * context. + */ +const Feature Feature::ExperimentalConcatFunction("concat", "Enable the <code>concat()</code> function."); + +Feature::Feature(const std::string &name, const std::string &description) + : enabled(false), name(name), description(description) +{ + feature_map[name] = this; + feature_list.push_back(this); +} + +Feature::~Feature() +{ +} + +const std::string &Feature::get_name() const +{ + return name; +} + +const std::string &Feature::get_description() const +{ + return description; +} + +bool Feature::is_enabled() const +{ + return enabled; +} + +void Feature::enable(bool status) +{ + enabled = status; +} + +void Feature::enable_feature(const std::string &feature_name, bool status) +{ + map_t::iterator it = feature_map.find(feature_name); + if (it != feature_map.end()) { + it->second->enable(status); + } else { + PRINTB("WARNING: Ignoring request to enable unknown feature '%s'.", feature_name); + } +} + +Feature::iterator Feature::begin() +{ + return feature_list.begin(); +} + +Feature::iterator Feature::end() +{ + return feature_list.end(); +} + +void Feature::dump_features() +{ + for (map_t::iterator it = feature_map.begin(); it != feature_map.end(); it++) { + std::cout << "Feature('" << it->first << "') = " << (it->second->is_enabled() ? "enabled" : "disabled") << std::endl; + } +} diff --git a/src/feature.h b/src/feature.h new file mode 100644 index 0000000..20b4f16 --- /dev/null +++ b/src/feature.h @@ -0,0 +1,44 @@ +#ifndef FEATURE_H_ +#define FEATURE_H_ + +#include <stdio.h> +#include <iostream> +#include <string> +#include <map> +#include <vector> + +class Feature +{ +public: + typedef std::vector<Feature *> list_t; + typedef list_t::iterator iterator; + + static const Feature ExperimentalConcatFunction; + + const std::string& get_name() const; + const std::string& get_description() const; + + bool is_enabled() const; + void enable(bool status); + + static iterator begin(); + static iterator end(); + + static void dump_features(); + static void enable_feature(const std::string &feature_name, bool status = true); + +private: + bool enabled; + + const std::string name; + const std::string description; + + typedef std::map<std::string, Feature *> map_t; + static map_t feature_map; + static list_t feature_list; + + Feature(const std::string &name, const std::string &description); + virtual ~Feature(); +}; + +#endif diff --git a/src/func.cc b/src/func.cc index 4587f72..878bc1c 100644 --- a/src/func.cc +++ b/src/func.cc @@ -343,6 +343,24 @@ Value builtin_str(const Context *, const EvalContext *evalctx) return Value(stream.str()); } +Value builtin_concat(const Context *, const EvalContext *evalctx) +{ + Value::VectorType result; + + for (size_t i = 0; i < evalctx->numArgs(); i++) { + const Value v = evalctx->getArgValue(i); + if (v.type() == Value::VECTOR) { + Value::VectorType vec = v.toVector(); + for (Value::VectorType::const_iterator it = vec.begin(); it != vec.end(); it++) { + result.push_back(*it); + } + } else { + result.push_back(v); + } + } + return Value(result); +} + Value builtin_lookup(const Context *, const EvalContext *evalctx) { double p, low_p, low_v, high_p, high_v; @@ -604,6 +622,7 @@ void register_builtin_functions() Builtins::init("log", new BuiltinFunction(&builtin_log)); Builtins::init("ln", new BuiltinFunction(&builtin_ln)); Builtins::init("str", new BuiltinFunction(&builtin_str)); + Builtins::init("concat", new BuiltinFunction(&builtin_concat, Feature::ExperimentalConcatFunction)); Builtins::init("lookup", new BuiltinFunction(&builtin_lookup)); Builtins::init("search", new BuiltinFunction(&builtin_search)); Builtins::init("version", new BuiltinFunction(&builtin_version)); diff --git a/src/function.h b/src/function.h index a1fde3c..2491809 100644 --- a/src/function.h +++ b/src/function.h @@ -3,13 +3,21 @@ #include "value.h" #include "typedefs.h" +#include "feature.h" + #include <string> #include <vector> + class AbstractFunction { +private: + const Feature *feature; public: + AbstractFunction() : feature(NULL) {} + AbstractFunction(const Feature& feature) : feature(&feature) {} virtual ~AbstractFunction(); + virtual bool is_enabled() const { return (feature == NULL) || feature->is_enabled(); }; virtual Value evaluate(const class Context *ctx, const class EvalContext *evalctx) const; virtual std::string dump(const std::string &indent, const std::string &name) const; }; @@ -21,6 +29,7 @@ public: eval_func_t eval_func; BuiltinFunction(eval_func_t f) : eval_func(f) { } + BuiltinFunction(eval_func_t f, const Feature& feature) : AbstractFunction(feature), eval_func(f) { } virtual ~BuiltinFunction(); virtual Value evaluate(const Context *ctx, const EvalContext *evalctx) const; diff --git a/src/modcontext.cc b/src/modcontext.cc index 7941cf5..21b04fd 100644 --- a/src/modcontext.cc +++ b/src/modcontext.cc @@ -95,7 +95,12 @@ void ModuleContext::registerBuiltin() const AbstractFunction *ModuleContext::findLocalFunction(const std::string &name) const { if (this->functions_p && this->functions_p->find(name) != this->functions_p->end()) { - return this->functions_p->find(name)->second; + AbstractFunction *f = this->functions_p->find(name)->second; + if (!f->is_enabled()) { + PRINTB("WARNING: Experimental builtin function '%s' is not enabled.", name); + return NULL; + } + return f; } return NULL; } @@ -104,6 +109,10 @@ const AbstractModule *ModuleContext::findLocalModule(const std::string &name) co { if (this->modules_p && this->modules_p->find(name) != this->modules_p->end()) { AbstractModule *m = this->modules_p->find(name)->second; + if (!m->is_enabled()) { + PRINTB("WARNING: Experimental builtin module '%s' is not enabled.", name); + return NULL; + } std::string replacement = Builtins::instance()->isDeprecated(name); if (!replacement.empty()) { PRINTB("DEPRECATED: The %s() module will be removed in future releases. Use %s() instead.", name % replacement); diff --git a/src/module.h b/src/module.h index 8414706..81e5f10 100644 --- a/src/module.h +++ b/src/module.h @@ -13,6 +13,7 @@ #include "value.h" #include "typedefs.h" #include "localscope.h" +#include "feature.h" class ModuleInstantiation { @@ -60,8 +61,13 @@ public: class AbstractModule { +private: + const Feature *feature; public: + AbstractModule() : feature(NULL) {} + AbstractModule(const Feature& feature) : feature(&feature) {} virtual ~AbstractModule(); + virtual bool is_enabled() const { return (feature == NULL) || feature->is_enabled(); }; virtual class AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const class EvalContext *evalctx = NULL) const; virtual std::string dump(const std::string &indent, const std::string &name) const; }; @@ -70,6 +76,7 @@ class Module : public AbstractModule { public: Module() { } + Module(const Feature& feature) : AbstractModule(feature) { } virtual ~Module(); virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx = NULL) const; diff --git a/src/openscad.cc b/src/openscad.cc index ab84235..16a6e89 100644 --- a/src/openscad.cc +++ b/src/openscad.cc @@ -33,6 +33,7 @@ #include "builtin.h" #include "printutils.h" #include "handle_dep.h" +#include "feature.h" #include "parsersettings.h" #include "rendersettings.h" #include "PlatformUtils.h" @@ -111,6 +112,7 @@ static void help(const char *progname) "%2% --camera=eyex,y,z,centerx,y,z ] \\\n" "%2%[ --imgsize=width,height ] [ --projection=(o)rtho|(p)ersp] \\\n" "%2%[ --render | --preview[=throwntogether] ] \\\n" + "%2%[ --enable=<feature> \\\n" "%2%filename\n", progname % (const char *)tabstr); exit(1); @@ -587,7 +589,8 @@ int main(int argc, char **argv) ("x,x", po::value<string>(), "dxf-file") ("d,d", po::value<string>(), "deps-file") ("m,m", po::value<string>(), "makefile") - ("D,D", po::value<vector<string> >(), "var=val"); + ("D,D", po::value<vector<string> >(), "var=val") + ("enable", po::value<vector<string> >(), "enable experimental features"); po::options_description hidden("Hidden options"); hidden.add_options() @@ -651,6 +654,11 @@ int main(int argc, char **argv) commandline_commands += ";\n"; } } + if (vm.count("enable")) { + BOOST_FOREACH(const string &feature, vm["enable"].as<vector<string> >()) { + Feature::enable_feature(feature); + } + } vector<string> inputFiles; if (vm.count("input-file")) { inputFiles = vm["input-file"].as<vector<string> >(); |