#### Fast .stl viewer #####
-# Original Project Author: Matt Keeter Copyright 2014 -2017
-# Author: Paul Tsouchlos Copyright 2017
+# Original Project Author: Matt Keeter Copyright 2014-2024
+# Author: Paul Tsouchlos Copyright 2017-2024
cmake_minimum_required(VERSION 3.3)
# Set the version number
set (FSTL_VERSION_MAJOR "0")
-set (FSTL_VERSION_MINOR "10")
+set (FSTL_VERSION_MINOR "11")
set (FSTL_VERSION_PATCH "0")
set (PROJECT_VERSION "${FSTL_VERSION_MAJOR}.${FSTL_VERSION_MINOR}.${FSTL_VERSION_PATCH}")
src/loader.cpp
src/main.cpp
src/mesh.cpp
-src/window.cpp)
+src/window.cpp
+src/shaderlightprefs.cpp)
#set project headers.
set(Project_Headers src/app.h
src/glmesh.h
src/loader.h
src/mesh.h
-src/window.h)
+src/window.h
+src/shaderlightprefs.h)
#set project resources and icon resource
set(Project_Resources qt/qt.qrc gl/gl.qrc)
<file>mesh.vert</file>
<file>mesh_wireframe.frag</file>
<file>mesh_surfaceangle.frag</file>
+ <file>mesh_light.frag</file>
<file>quad.frag</file>
<file>quad.vert</file>
<file>colored_lines.frag</file>
--- /dev/null
+#version 120
+
+uniform float zoom;
+uniform vec4 ambient_light_color;
+uniform vec4 directive_light_color;
+uniform vec3 directive_light_direction;
+
+varying vec3 ec_pos;
+
+void main() {
+ // Normalize light direction
+ vec3 dir = normalize(directive_light_direction);
+
+ // vec3 a = vec3(0.0, 1.0, 1.0);
+ // normal vector
+ vec3 ec_normal = normalize(cross(dFdx(ec_pos), dFdy(ec_pos)));
+ ec_normal.z *= zoom;
+ ec_normal = normalize(ec_normal);
+
+
+ vec3 color = ambient_light_color.w * ambient_light_color.xyz + directive_light_color.w * dot(ec_normal,dir) * directive_light_color.xyz;
+
+ // float coef = dot(ec_normal,dir);
+ // vec3 color = coef * lightcolor + (1.0 - coef) * objectcolor;
+ gl_FragColor = vec4(color, 1.0);
+}
<RCC>
- <qresource prefix="qt/">
+ <qresource prefix="/qt">
<file>style.qss</file>
+ <file>icons/fstl_64x64.png</file>
</qresource>
</RCC>
#include <QDebug>
#include <QFileOpenEvent>
+#include <QDir>
#include "app.h"
#include "window.h"
App::App(int& argc, char *argv[]) :
QApplication(argc, argv), window(new Window())
{
- if (argc > 1)
- window->load_stl(argv[1]);
+ if (argc > 1) {
+ QString filename = argv[1];
+ if (filename.startsWith("~")) {
+ filename.replace(0, 1, QDir::homePath());
+ }
+ window->load_stl(filename);
+ }
else
+ {
window->load_stl(":gl/sphere.stl");
+ }
window->show();
}
const float Canvas::P_PERSPECTIVE = 0.25f;
const float Canvas::P_ORTHOGRAPHIC = 0.0f;
+const QString Canvas::AMBIENT_COLOR = "ambientColor";
+const QString Canvas::AMBIENT_FACTOR = "ambientFactor";
+const QString Canvas::DIRECTIVE_COLOR = "directiveColor";
+const QString Canvas::DIRECTIVE_FACTOR = "directiveFactor";
+const QString Canvas::CURRENT_LIGHT_DIRECTION = "currentLightDirection";
+
+const QColor Canvas::defaultAmbientColor = QColor::fromRgbF(0.22,0.8,1.0);
+const QColor Canvas::defaultDirectiveColor = QColor(255,255,255);
+const float Canvas::defaultAmbientFactor = 0.67;
+const float Canvas::defaultDirectiveFactor = 0.5;
+const int Canvas::defaultCurrentLightDirection = 1;
+
Canvas::Canvas(const QSurfaceFormat& format, QWidget *parent)
: QOpenGLWidget(parent), mesh(nullptr),
- scale(1), zoom(1), tilt(90), yaw(0),
+ scale(1), zoom(1),
anim(this, "perspective"), status(" "),
meshInfo("")
{
QFile styleFile(":/qt/style.qss");
styleFile.open( QFile::ReadOnly );
setStyleSheet(styleFile.readAll());
+ currentTransform = QMatrix4x4();
+ resetTransform();
+
+ QSettings settings;
+ ambientColor = settings.value(AMBIENT_COLOR,defaultAmbientColor).value<QColor>();
+ directiveColor = settings.value(DIRECTIVE_COLOR,defaultDirectiveColor).value<QColor>();
+ ambientFactor = settings.value(AMBIENT_FACTOR,defaultAmbientFactor).value<float>();
+ directiveFactor = settings.value(DIRECTIVE_FACTOR,defaultDirectiveFactor).value<float>();
+
+ // Fill direction list
+ // Fill in directions
+ nameDir.clear();
+ listDir.clear();
+ QList<QString> xname, yname, zname;
+ xname << "right " << " " << "left ";
+ yname << "top " << " " << "bottom ";
+ zname << "rear " << " " << "front ";
+ for (int i=-1; i<2 ; i++) {
+ for (int j=-1; j<2; j++) {
+ for (int k=-1; k<2; k++) {
+ QString current = xname.at(i+1) + yname.at(j+1) + zname.at(k+1);
+ if (!(i==0 && j==0 && k==0)) {
+ nameDir << current.simplified();
+ listDir << QVector3D((double)i,(double)j,(double)k);
+ }
+ }
+ }
+ }
+ currentLightDirection = settings.value(CURRENT_LIGHT_DIRECTION,defaultCurrentLightDirection).value<int>();
+ if (currentLightDirection < 0 || currentLightDirection >= nameDir.length()) {
+ currentLightDirection = defaultCurrentLightDirection;
+ }
anim.setDuration(100);
}
anim.start();
}
+void Canvas::common_view_change(enum ViewPoint c)
+{
+ if (c == centerview) {
+ scale = default_scale;
+ center = default_center;
+ zoom = 1;
+ update();
+ return;
+ }
+
+ currentTransform.setToIdentity();
+ currentTransform.rotate(180.0, QVector3D(0, 0, 1));
+
+ switch (c)
+ {
+ case isoview:
+ {
+ currentTransform.rotate(90, QVector3D(1, 0, 0));
+ currentTransform.rotate(-45, QVector3D(0, 0, 1));
+ currentTransform.rotate(35.264, QVector3D(1, 1, 0));
+ }
+ break;
+ case topview:
+ {
+ currentTransform.rotate(180, QVector3D(1, 0, 0));
+ }
+ break;
+ case leftview:
+ {
+ currentTransform.rotate(180, QVector3D(1, 0, 0));
+ currentTransform.rotate(90, QVector3D(0, 0, 1));
+ currentTransform.rotate(90, QVector3D(0, 1, 0));
+ }
+ break;
+ case rightview:
+ {
+ currentTransform.rotate(180, QVector3D(1, 0, 0));
+ currentTransform.rotate(-90.0, QVector3D(0, 1, 0));
+ currentTransform.rotate(-90, QVector3D(1, 0, 0));
+ }
+ break;
+ case frontview:
+ {
+ currentTransform.rotate(90, QVector3D(1, 0, 0));
+ }
+ break;
+ case backview:
+ {
+ currentTransform.rotate(90, QVector3D(1, 0, 0));
+ currentTransform.rotate(180, QVector3D(0, 0, 1));
+ }
+ case bottomview:
+ [[fallthrough]];
+ default:
+ break;
+ }
+ update();
+}
+
void Canvas::view_perspective(float p, bool animate){
if(animate)
{
update();
}
+void Canvas::setResetTransformOnLoad(bool d) {
+ resetTransformOnLoad = d;
+}
+
+void Canvas::resetTransform() {
+ currentTransform.setToIdentity();
+ // apply some rotations to define initial orientation
+ currentTransform.rotate(-90.0, QVector3D(1, 0, 0));
+ currentTransform.rotate(180.0 + 15.0, QVector3D(0, 0, 1));
+ currentTransform.rotate(15.0, QVector3D(1, -sin(M_PI/12), 0));
+
+ zoom = 1;
+}
+
void Canvas::load_mesh(Mesh* m, bool is_reload)
{
delete mesh;
QVector3D upper(m->xmax(), m->ymax(), m->zmax());
if (!is_reload)
{
- center = (lower + upper) / 2;
- scale = 2 / (upper - lower).length();
+ default_center = center = (lower + upper) / 2;
+ default_scale = scale = 2 / (upper - lower).length();
// Reset other camera parameters
zoom = 1;
- yaw = 0;
- tilt = 90;
+ if (resetTransformOnLoad) {
+ resetTransform();
+ }
}
meshInfo = QStringLiteral("Triangles: %1\nX: [%2, %3]\nY: [%4, %5]\nZ: [%6, %7]").arg(m->triCount());
for(int dIdx = 0; dIdx < 3; dIdx++) meshInfo = meshInfo.arg(lower[dIdx]).arg(upper[dIdx]);
mesh_surfaceangle_shader.addShader(mesh_vertshader);
mesh_surfaceangle_shader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/gl/mesh_surfaceangle.frag");
mesh_surfaceangle_shader.link();
+ mesh_meshlight_shader.addShader(mesh_vertshader);
+ mesh_meshlight_shader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/gl/mesh_light.frag");
+ mesh_meshlight_shader.link();
backdrop = new Backdrop();
axis = new Axis();
{
selected_mesh_shader = &mesh_shader;
}
- else
+ else if (drawMode == surfaceangle)
{
selected_mesh_shader = &mesh_surfaceangle_shader;
}
+ else if (drawMode == meshlight)
+ {
+ selected_mesh_shader = &mesh_meshlight_shader;
+ }
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
// Compensate for z-flattening when zooming
glUniform1f(selected_mesh_shader->uniformLocation("zoom"), 1/zoom);
+ // specific meshlight arguments
+ if (drawMode == meshlight) {
+ // Ambient Light Color, followed by the ambient light coefficient to use
+ //glUniform4f(selected_mesh_shader->uniformLocation("ambient_light_color"),0.22f, 0.8f, 1.0f, 0.67f);
+ glUniform4f(selected_mesh_shader->uniformLocation("ambient_light_color"),ambientColor.redF(), ambientColor.greenF(), ambientColor.blueF(), ambientFactor);
+ // Directive Light Color, followed by the directive light coefficient to use
+ //glUniform4f(selected_mesh_shader->uniformLocation("directive_light_color"),1.0f,1.0f,1.0f,0.5f);
+ glUniform4f(selected_mesh_shader->uniformLocation("directive_light_color"),directiveColor.redF(),directiveColor.greenF(),directiveColor.blueF(),directiveFactor);
+
+ // Directive Light Direction
+ // dir 1,0,0 Light from the left
+ // dir -1,0,0 Light from the right
+ // dir 0,1,0 Light from bottom
+ // dir 0,-1,0 Light from top
+ // dir 0,0,1 Light from viewer (front)
+ // dir 0,0,-1 Light from behind
+ //
+ // -1,-1,0 Light from top right
+ //glUniform3f(selected_mesh_shader->uniformLocation("directive_light_direction"),-1.0f,-1.0f,0.0f);
+ glUniform3f(selected_mesh_shader->uniformLocation("directive_light_direction"),listDir.at(currentLightDirection).x(), listDir.at(currentLightDirection).y(), listDir.at(currentLightDirection).z());
+ }
+
// Find and enable the attribute location for vertex position
const GLuint vp = selected_mesh_shader->attributeLocation("vertex_position");
glEnableVertexAttribArray(vp);
}
QMatrix4x4 Canvas::orient_matrix() const
{
- QMatrix4x4 m;
- m.rotate(tilt, QVector3D(1, 0, 0));
- m.rotate(yaw, QVector3D(0, 0, 1));
- //We want the x axis to the right, and the z axis up
- m.scale(-1, 1, -1);
+ QMatrix4x4 m = currentTransform;
return m;
}
QMatrix4x4 Canvas::transform_matrix() const
}
}
+
+// This method change the referential of the mouse point coordinates
+// into a referential x=[-1.0,1.0], y=[-1.0,1.0], with 0,0 being the
+// center of the widget.
+QPointF Canvas::changeMouseCoordinates(QPoint p) {
+ QPointF pr;
+ // Change coordinates
+ double ws2 = this->width() / 2.0;
+ double hs2 = this->height() / 2.0;
+ pr.setX(p.x() / ws2 - 1.0);
+ pr.setY(p.y() / hs2 - 1.0);
+ return pr;
+}
+
+void Canvas::calcArcballTransform(QPointF p1, QPointF p2) {
+ // Calc z1 & z2
+ double x1 = p1.x();
+ double x2 = p2.x();
+ double y1 = p1.y();
+ double y2 = p2.y();
+ double p1sq = x1 * x1 + y1 * y1;
+ double z1;
+ if (p1sq <= 1) {
+ z1 = sqrt(1.0 - p1sq);
+ } else {
+ x1 = x1 / sqrt(p1sq);
+ y1 = y1 / sqrt(p1sq);
+ z1 = 0.0;
+ }
+ double p2sq = x2 * x2 + y2 * y2;
+ double z2;
+ if (p2sq <= 1) {
+ z2 = sqrt(1.0 - p2sq);
+ } else {
+ x2 = x2 / sqrt(p2sq);
+ y2 = y2 / sqrt(p2sq);
+ z2 = 0.0;
+ }
+
+ // set v1 and v2
+ QVector3D v1(x1, y1, z1);
+ QVector3D v2(x2, y2, z2);
+
+ // calc v1 cross v2
+ QVector3D v1xv2 = QVector3D::crossProduct(v1, v2);
+ QVector3D v1xv2Obj = currentTransform.inverted().mapVector(v1xv2);
+
+ // calc angle
+ double angle = acos(std::min(1.0f,QVector3D::dotProduct(v1, v2))) * 180.0 / M_PI;
+
+ // apply transform
+ currentTransform.rotate(angle,v1xv2Obj);
+}
+
void Canvas::mouseMoveEvent(QMouseEvent* event)
{
auto p = event->pos();
auto d = p - mouse_pos;
-
+
if (event->buttons() & Qt::LeftButton)
{
- yaw = fmod(yaw - d.x(), 360);
- tilt = fmod(tilt - d.y(), 360);
+ QPointF p1r = changeMouseCoordinates(mouse_pos);
+ QPointF p2r = changeMouseCoordinates(p);
+ calcArcballTransform(p1r,p2r);
+
update();
}
else if (event->buttons() & Qt::RightButton)
{
glViewport(0, 0, width, height);
}
+
+QColor Canvas::getAmbientColor() {
+ return ambientColor;
+}
+
+void Canvas::setAmbientColor(QColor c) {
+ ambientColor = c;
+ QSettings settings;
+ settings.setValue(AMBIENT_COLOR,c);
+}
+
+double Canvas::getAmbientFactor() {
+ return (float) ambientFactor;
+}
+
+void Canvas::setAmbientFactor(double f) {
+ ambientFactor = (float) f;
+ QSettings settings;
+ settings.setValue(AMBIENT_FACTOR,f);
+}
+
+void Canvas::resetAmbientColor() {
+ setAmbientColor(defaultAmbientColor);
+ setAmbientFactor(defaultAmbientFactor);
+}
+
+QColor Canvas::getDirectiveColor() {
+ return directiveColor;
+}
+
+void Canvas::setDirectiveColor(QColor c) {
+ directiveColor = c;
+ QSettings settings;
+ settings.setValue(DIRECTIVE_COLOR,c);
+}
+
+double Canvas::getDirectiveFactor() {
+ return (float) directiveFactor;
+}
+
+void Canvas::setDirectiveFactor(double f) {
+ directiveFactor = (float) f;
+ QSettings settings;
+ settings.setValue(DIRECTIVE_FACTOR,f);
+}
+
+void Canvas::resetDirectiveColor() {
+ setDirectiveColor(defaultDirectiveColor);
+ setDirectiveFactor(defaultDirectiveFactor);
+}
+
+QList<QString> Canvas::getNameDir() {
+ return nameDir;
+}
+
+int Canvas::getCurrentLightDirection() {
+ return currentLightDirection;
+}
+
+void Canvas::setCurrentLightDirection(int ind) {
+ currentLightDirection = ind;
+ QSettings settings;
+ settings.setValue(CURRENT_LIGHT_DIRECTION,currentLightDirection);
+}
+
+void Canvas::resetCurrentLightDirection() {
+ setCurrentLightDirection(defaultCurrentLightDirection);
+}
class Backdrop;
class Axis;
-enum DrawMode {shaded, wireframe, surfaceangle, DRAWMODECOUNT};
+enum ViewPoint {centerview, isoview, topview, bottomview, leftview, rightview, frontview, backview};
+enum DrawMode {shaded, wireframe, surfaceangle, meshlight, DRAWMODECOUNT};
class Canvas : public QOpenGLWidget, protected QOpenGLFunctions
{
void draw_axes(bool d);
void invert_zoom(bool d);
void set_drawMode(enum DrawMode mode);
+ void common_view_change(enum ViewPoint c);
+ void setResetTransformOnLoad(bool d);
+
+ QColor getAmbientColor();
+ void setAmbientColor(QColor c);
+ double getAmbientFactor();
+ void setAmbientFactor(double f);
+ void resetAmbientColor();
+
+ QColor getDirectiveColor();
+ void setDirectiveColor(QColor c);
+ double getDirectiveFactor();
+ void setDirectiveFactor(double f);
+ void resetDirectiveColor();
+
+ QList<QString> getNameDir();
+ int getCurrentLightDirection();
+ void setCurrentLightDirection(int ind);
+ void resetCurrentLightDirection();
public slots:
void set_status(const QString& s);
QMatrix4x4 transform_matrix() const;
QMatrix4x4 aspect_matrix() const;
QMatrix4x4 view_matrix() const;
+ void resetTransform();
+ QPointF changeMouseCoordinates(QPoint p);
+ void calcArcballTransform(QPointF p1, QPointF p2);
QOpenGLShader* mesh_vertshader;
QOpenGLShaderProgram mesh_shader;
QOpenGLShaderProgram mesh_wireframe_shader;
QOpenGLShaderProgram mesh_surfaceangle_shader;
+ QOpenGLShaderProgram mesh_meshlight_shader;
+
+ QColor ambientColor;
+ QColor directiveColor;
+ float ambientFactor;
+ float directiveFactor;
+ QList<QString> nameDir;
+ QList<QVector3D> listDir;
+ int currentLightDirection;
+
+ const static QColor defaultAmbientColor;
+ const static QColor defaultDirectiveColor;
+ const static float defaultAmbientFactor;
+ const static float defaultDirectiveFactor;
+ const static int defaultCurrentLightDirection;
+ const static QString AMBIENT_COLOR;
+ const static QString AMBIENT_FACTOR;
+ const static QString DIRECTIVE_COLOR;
+ const static QString DIRECTIVE_FACTOR;
+ const static QString CURRENT_LIGHT_DIRECTION;
+
GLMesh* mesh;
Backdrop* backdrop;
Axis* axis;
- QVector3D center;
- float scale;
+ QVector3D center, default_center;
+ float scale, default_scale;
float zoom;
- float tilt;
- float yaw;
+ QMatrix4x4 currentTransform;
float perspective;
enum DrawMode drawMode;
bool drawAxes;
bool invertZoom;
+ bool resetTransformOnLoad;
Q_PROPERTY(float perspective MEMBER perspective WRITE set_perspective);
QPropertyAnimation anim;
return NULL;
}
+ qint64 file_size, file_size_old;
+ file_size = file.size();
+ do {
+ file_size_old = file_size;
+ QThread::usleep(100000);
+ file_size = file.size();
+ }
+ while(file_size != file_size_old);
+
// First, try to read the stl as an ASCII file
if (file.read(5) == "solid")
{
int main(int argc, char *argv[])
{
+ // Force C locale to force decimal point
+ QLocale::setDefault(QLocale::c());
+
QCoreApplication::setOrganizationName("fstl-app");
QCoreApplication::setOrganizationDomain("https://github.com/fstl-app/fstl");
QCoreApplication::setApplicationName("fstl");
QCoreApplication::setApplicationVersion(FSTL_VERSION);
App a(argc, argv);
+
return a.exec();
}
--- /dev/null
+#include "shaderlightprefs.h"
+#include "canvas.h"
+#include <QColorDialog>
+
+const QString ShaderLightPrefs::PREFS_GEOM = "shaderPrefsGeometry";
+
+ShaderLightPrefs::ShaderLightPrefs(QWidget *parent, Canvas *_canvas) : QDialog(parent)
+{
+ canvas = _canvas;
+
+ QVBoxLayout* prefsLayout = new QVBoxLayout;
+ this->setLayout(prefsLayout);
+
+ QLabel* title = new QLabel("Shader preferences");
+ QFont boldFont = QApplication::font();
+ boldFont.setWeight(QFont::Bold);
+ title->setFont(boldFont);
+ title->setAlignment(Qt::AlignCenter);
+ prefsLayout->addWidget(title);
+
+ QWidget* middleWidget = new QWidget;
+ QGridLayout* middleLayout = new QGridLayout;
+ middleWidget->setLayout(middleLayout);
+ this->layout()->addWidget(middleWidget);
+
+ // labels
+ middleLayout->addWidget(new QLabel("Ambient Color"),0,0);
+ middleLayout->addWidget(new QLabel("Directive Color"),1,0);
+ middleLayout->addWidget(new QLabel("Direction"),2,0);
+
+ QPixmap dummy(20, 20);
+
+ dummy.fill(canvas->getAmbientColor());
+ buttonAmbientColor = new QPushButton;
+ buttonAmbientColor->setIcon(QIcon(dummy));
+ middleLayout->addWidget(buttonAmbientColor,0,1);
+ buttonAmbientColor->setFocusPolicy(Qt::NoFocus);
+ connect(buttonAmbientColor,SIGNAL(clicked(bool)),this,SLOT(buttonAmbientColorClicked()));
+
+ editAmbientFactor = new QLineEdit;
+ editAmbientFactor->setValidator(new QDoubleValidator);
+ editAmbientFactor->setText(QString("%1").arg(canvas->getAmbientFactor()));
+ middleLayout->addWidget(editAmbientFactor,0,2);
+ connect(editAmbientFactor,SIGNAL(editingFinished()),this,SLOT(editAmbientFactorFinished()));
+
+ QPushButton* buttonResetAmbientColor = new QPushButton("Reset");
+ middleLayout->addWidget(buttonResetAmbientColor,0,3);
+ buttonResetAmbientColor->setFocusPolicy(Qt::NoFocus);
+ connect(buttonResetAmbientColor,SIGNAL(clicked(bool)),this,SLOT(resetAmbientColorClicked()));
+
+
+ dummy.fill(canvas->getDirectiveColor());
+ buttonDirectiveColor = new QPushButton;
+ buttonDirectiveColor->setIcon(QIcon(dummy));
+ middleLayout->addWidget(buttonDirectiveColor,1,1);
+ buttonDirectiveColor->setFocusPolicy(Qt::NoFocus);
+ connect(buttonDirectiveColor,SIGNAL(clicked(bool)),this,SLOT(buttonDirectiveColorClicked()));
+
+ editDirectiveFactor = new QLineEdit;
+ editDirectiveFactor->setValidator(new QDoubleValidator);
+ editDirectiveFactor->setText(QString("%1").arg(canvas->getDirectiveFactor()));
+ middleLayout->addWidget(editDirectiveFactor,1,2);
+ connect(editDirectiveFactor,SIGNAL(editingFinished()),this,SLOT(editDirectiveFactorFinished()));
+
+ QPushButton* buttonResetDirectiveColor = new QPushButton("Reset");
+ middleLayout->addWidget(buttonResetDirectiveColor,1,3);
+ buttonResetDirectiveColor->setFocusPolicy(Qt::NoFocus);
+ connect(buttonResetDirectiveColor,SIGNAL(clicked(bool)),this,SLOT(resetDirectiveColorClicked()));
+
+ // Fill in directions
+
+ comboDirections = new QComboBox;
+ middleLayout->addWidget(comboDirections,2,1,1,2);
+ comboDirections->addItems(canvas->getNameDir());
+ comboDirections->setCurrentIndex(canvas->getCurrentLightDirection());
+ connect(comboDirections,SIGNAL(currentIndexChanged(int)),this,SLOT(comboDirectionsChanged(int)));
+
+ QPushButton* buttonResetDirection = new QPushButton("Reset");
+ middleLayout->addWidget(buttonResetDirection,2,3);
+ buttonResetDirection->setFocusPolicy(Qt::NoFocus);
+ connect(buttonResetDirection,SIGNAL(clicked(bool)),this,SLOT(resetDirection()));
+
+
+ // Ok button
+ QWidget* boxButton = new QWidget;
+ QHBoxLayout* boxButtonLayout = new QHBoxLayout;
+ boxButton->setLayout(boxButtonLayout);
+ QFrame *spacerL = new QFrame;
+ spacerL->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding));
+ QPushButton* okButton = new QPushButton("Ok");
+ boxButtonLayout->addWidget(spacerL);
+ boxButtonLayout->addWidget(okButton);
+ this->layout()->addWidget(boxButton);
+ okButton->setFocusPolicy(Qt::NoFocus);
+ connect(okButton,SIGNAL(clicked(bool)),this,SLOT(okButtonClicked()));
+
+ QSettings settings;
+ if (!settings.value(PREFS_GEOM).isNull()) {
+ restoreGeometry(settings.value(PREFS_GEOM).toByteArray());
+ }
+}
+
+void ShaderLightPrefs::buttonAmbientColorClicked() {
+ QColor newColor = QColorDialog::getColor(canvas->getAmbientColor(), this, QString("Choose color"),QColorDialog::DontUseNativeDialog);
+ if (newColor.isValid() == true)
+ {
+ canvas->setAmbientColor(newColor);
+ QPixmap dummy(20, 20);
+ dummy.fill(canvas->getAmbientColor());
+ buttonAmbientColor->setIcon(QIcon(dummy));
+ canvas->update();
+ }
+}
+
+void ShaderLightPrefs::editAmbientFactorFinished() {
+ canvas->setAmbientFactor(editAmbientFactor->text().toDouble());
+ canvas->update();
+}
+
+void ShaderLightPrefs::resetAmbientColorClicked() {
+ canvas->resetAmbientColor();
+ QPixmap dummy(20, 20);
+ dummy.fill(canvas->getAmbientColor());
+ buttonAmbientColor->setIcon(QIcon(dummy));
+ editAmbientFactor->setText(QString("%1").arg(canvas->getAmbientFactor()));
+ canvas->update();
+}
+
+void ShaderLightPrefs::buttonDirectiveColorClicked() {
+ QColor newColor = QColorDialog::getColor(canvas->getDirectiveColor(), this, QString("Choose color"),QColorDialog::DontUseNativeDialog);
+ if (newColor.isValid() == true)
+ {
+ canvas->setDirectiveColor(newColor);
+ QPixmap dummy(20, 20);
+ dummy.fill(canvas->getDirectiveColor());
+ buttonDirectiveColor->setIcon(QIcon(dummy));
+ canvas->update();
+ }
+}
+
+void ShaderLightPrefs::editDirectiveFactorFinished() {
+ canvas->setDirectiveFactor(editDirectiveFactor->text().toDouble());
+ canvas->update();
+}
+
+void ShaderLightPrefs::resetDirectiveColorClicked() {
+ canvas->resetDirectiveColor();
+ QPixmap dummy(20, 20);
+ dummy.fill(canvas->getDirectiveColor());
+ buttonDirectiveColor->setIcon(QIcon(dummy));
+ editDirectiveFactor->setText(QString("%1").arg(canvas->getDirectiveFactor()));
+ canvas->update();
+}
+
+void ShaderLightPrefs::okButtonClicked() {
+ this->close();
+}
+
+void ShaderLightPrefs::comboDirectionsChanged(int ind) {
+ canvas->setCurrentLightDirection(ind);
+ canvas->update();
+}
+
+void ShaderLightPrefs::resetDirection() {
+ canvas->resetCurrentLightDirection();
+ comboDirections->setCurrentIndex(canvas->getCurrentLightDirection());
+ canvas->update();
+}
+
+void ShaderLightPrefs::resizeEvent(QResizeEvent *event)
+{
+ QSettings().setValue(PREFS_GEOM, saveGeometry());
+}
+
+void ShaderLightPrefs::moveEvent(QMoveEvent *event)
+{
+ QSettings().setValue(PREFS_GEOM, saveGeometry());
+}
+
+
--- /dev/null
+#ifndef SHADERLIGHTPREFS_H
+#define SHADERLIGHTPREFS_H
+
+#include <QDialog>
+
+class Canvas;
+class QLabel;
+class QLineEdit;
+class QComboBox;
+
+class ShaderLightPrefs : public QDialog
+{
+ Q_OBJECT
+public:
+ ShaderLightPrefs(QWidget* parent, Canvas* _canvas);
+
+protected:
+ void resizeEvent(QResizeEvent *event) override;
+ void moveEvent(QMoveEvent *event) override;
+
+private slots:
+ void buttonAmbientColorClicked();
+ void editAmbientFactorFinished();
+ void resetAmbientColorClicked();
+
+ void buttonDirectiveColorClicked();
+ void editDirectiveFactorFinished();
+ void resetDirectiveColorClicked();
+
+ void comboDirectionsChanged(int ind);
+ void resetDirection();
+
+ void okButtonClicked();
+
+private:
+ Canvas* canvas;
+ QPushButton* buttonAmbientColor;
+ QLineEdit* editAmbientFactor;
+ QPushButton* buttonDirectiveColor;
+ QLineEdit* editDirectiveFactor;
+ QComboBox* comboDirections;
+
+ const static QString PREFS_GEOM;
+};
+
+#endif // SHADERLIGHTPREFS_H
#include "window.h"
#include "canvas.h"
#include "loader.h"
+#include "shaderlightprefs.h"
+const QString Window::OPEN_EXTERNAL_KEY = "externalCmd";
const QString Window::RECENT_FILE_KEY = "recentFiles";
const QString Window::INVERT_ZOOM_KEY = "invertZoom";
const QString Window::AUTORELOAD_KEY = "autoreload";
const QString Window::PROJECTION_KEY = "projection";
const QString Window::DRAW_MODE_KEY = "drawMode";
const QString Window::WINDOW_GEOM_KEY = "windowGeometry";
+const QString Window::RESET_TRANSFORM_ON_LOAD_KEY = "resetTransformOnLoad";
Window::Window(QWidget *parent) :
QMainWindow(parent),
open_action(new QAction("Open", this)),
+ open_external_action(new QAction("Open with", this)),
about_action(new QAction("About", this)),
quit_action(new QAction("Quit", this)),
perspective_action(new QAction("Perspective", this)),
+ common_view_center_action(new QAction("Center the model", this)),
+ common_view_iso_action(new QAction("Isometric", this)),
+ common_view_top_action(new QAction("Top", this)),
+ common_view_bottom_action(new QAction("Bottom", this)),
+ common_view_left_action(new QAction("Left", this)),
+ common_view_right_action(new QAction("Right", this)),
+ common_view_front_action(new QAction("Front", this)),
+ common_view_back_action(new QAction("Back", this)),
orthographic_action(new QAction("Orthographic", this)),
shaded_action(new QAction("Shaded", this)),
wireframe_action(new QAction("Wireframe", this)),
surfaceangle_action(new QAction("Surface Angle", this)),
+ meshlight_action(new QAction("Shaded ambient and directive light source", this)),
+ drawModePrefs_action(new QAction("Draw Mode Settings")),
axes_action(new QAction("Draw Axes", this)),
invert_zoom_action(new QAction("Invert Zoom", this)),
reload_action(new QAction("Reload", this)),
autoreload_action(new QAction("Autoreload", this)),
save_screenshot_action(new QAction("Save Screenshot", this)),
hide_menuBar_action(new QAction("Hide Menu Bar", this)),
+ fullscreen_action(new QAction("Toggle Fullscreen",this)),
+ resetTransformOnLoadAction(new QAction("Reset rotation on load",this)),
recent_files(new QMenu("Open recent", this)),
recent_files_group(new QActionGroup(this)),
recent_files_clear_action(new QAction("Clear recent files", this)),
{
setWindowTitle("fstl");
+ setWindowIcon(QIcon(":/qt/icons/fstl_64x64.png"));
setAcceptDrops(true);
QSurfaceFormat format;
canvas = new Canvas(format, this);
setCentralWidget(canvas);
+ meshlightprefs = new ShaderLightPrefs(this, canvas);
+
+ QObject::connect(drawModePrefs_action, &QAction::triggered,this,&Window::on_drawModePrefs);
+
QObject::connect(watcher, &QFileSystemWatcher::fileChanged,
this, &Window::on_watched_change);
this, &Window::on_open);
this->addAction(open_action);
- quit_action->setShortcut(QKeySequence::Quit);
+ open_external_action->setShortcut(QKeySequence::Open);
+ QObject::connect(open_external_action, &QAction::triggered, this, &Window::on_open_external);
+ this->addAction(open_external_action);
+ open_external_action->setShortcut(QKeySequence(Qt::ALT + Qt::Key_S));
+
+ QList<QKeySequence> quitShortcuts = { QKeySequence::Quit, QKeySequence::Close };
+ quit_action->setShortcuts(quitShortcuts);
QObject::connect(quit_action, &QAction::triggered,
this, &Window::close);
this->addAction(quit_action);
rebuild_recent_files();
- auto file_menu = menuBar()->addMenu("File");
+ const auto file_menu = menuBar()->addMenu("File");
file_menu->addAction(open_action);
+ file_menu->addAction(open_external_action);
file_menu->addMenu(recent_files);
file_menu->addSeparator();
file_menu->addAction(reload_action);
file_menu->addAction(save_screenshot_action);
file_menu->addAction(quit_action);
- auto view_menu = menuBar()->addMenu("View");
- auto projection_menu = view_menu->addMenu("Projection");
+ const auto view_menu = menuBar()->addMenu("View");
+ const auto projection_menu = view_menu->addMenu("Projection");
projection_menu->addAction(perspective_action);
projection_menu->addAction(orthographic_action);
- auto projections = new QActionGroup(projection_menu);
+ const auto projections = new QActionGroup(projection_menu);
for (auto p : {perspective_action, orthographic_action})
{
projections->addAction(p);
QObject::connect(projections, &QActionGroup::triggered,
this, &Window::on_projection);
- auto draw_menu = view_menu->addMenu("Draw Mode");
+ const auto draw_menu = view_menu->addMenu("Draw Mode");
draw_menu->addAction(shaded_action);
draw_menu->addAction(wireframe_action);
draw_menu->addAction(surfaceangle_action);
- auto drawModes = new QActionGroup(draw_menu);
- for (auto p : {shaded_action, wireframe_action, surfaceangle_action})
+ draw_menu->addAction(meshlight_action);
+ const auto drawModes = new QActionGroup(draw_menu);
+ for (auto p : {shaded_action, wireframe_action, surfaceangle_action, meshlight_action})
{
drawModes->addAction(p);
p->setCheckable(true);
drawModes->setExclusive(true);
QObject::connect(drawModes, &QActionGroup::triggered,
this, &Window::on_drawMode);
+ view_menu->addAction(drawModePrefs_action);
+ drawModePrefs_action->setDisabled(true);
+
+ const auto common_menu = view_menu->addMenu("Viewpoint");
+ common_menu->addAction(common_view_iso_action);
+ common_menu->addAction(common_view_top_action);
+ common_menu->addAction(common_view_bottom_action);
+ common_menu->addAction(common_view_front_action);
+ common_menu->addAction(common_view_back_action);
+ common_menu->addAction(common_view_left_action);
+ common_menu->addAction(common_view_right_action);
+ common_menu->addAction(common_view_center_action);
+ const auto common_views = new QActionGroup(common_menu);
+ common_views->addAction(common_view_iso_action);
+ common_views->addAction(common_view_top_action);
+ common_views->addAction(common_view_bottom_action);
+ common_views->addAction(common_view_front_action);
+ common_views->addAction(common_view_back_action);
+ common_views->addAction(common_view_left_action);
+ common_views->addAction(common_view_right_action);
+ common_views->addAction(common_view_center_action);
+ common_view_iso_action->setShortcut(Qt::Key_0);
+ common_view_top_action->setShortcut(Qt::Key_1);
+ common_view_bottom_action->setShortcut(Qt::Key_2);
+ common_view_front_action->setShortcut(Qt::Key_3);
+ common_view_back_action->setShortcut(Qt::Key_4);
+ common_view_left_action->setShortcut(Qt::Key_5);
+ common_view_right_action->setShortcut(Qt::Key_6);
+ common_view_center_action->setShortcut(Qt::Key_9);
+ QObject::connect(common_views, &QActionGroup::triggered,
+ this, &Window::on_common_view_change);
+
view_menu->addAction(axes_action);
axes_action->setCheckable(true);
QObject::connect(axes_action, &QAction::triggered,
QObject::connect(invert_zoom_action, &QAction::triggered,
this, &Window::on_invertZoom);
+ view_menu->addAction(resetTransformOnLoadAction);
+ resetTransformOnLoadAction->setCheckable(true);
+ QObject::connect(resetTransformOnLoadAction, &QAction::triggered,
+ this, &Window::on_resetTransformOnLoad);
+
view_menu->addAction(hide_menuBar_action);
hide_menuBar_action->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_C);
hide_menuBar_action->setCheckable(true);
this, &Window::on_hide_menuBar);
this->addAction(hide_menuBar_action);
+ view_menu->addAction(fullscreen_action);
+ fullscreen_action->setShortcut(Qt::Key_F11);
+ fullscreen_action->setCheckable(true);
+ QObject::connect(fullscreen_action, &QAction::toggled,
+ this, &Window::on_fullscreen);
+ this->addAction(fullscreen_action);
+
+
auto help_menu = menuBar()->addMenu("Help");
help_menu->addAction(about_action);
canvas->invert_zoom(invert_zoom);
invert_zoom_action->setChecked(invert_zoom);
+ bool resetTransformOnLoad = settings.value(RESET_TRANSFORM_ON_LOAD_KEY, true).toBool();
+ canvas->setResetTransformOnLoad(resetTransformOnLoad);
+ resetTransformOnLoadAction->setChecked(resetTransformOnLoad);
+
autoreload_action->setChecked(settings.value(AUTORELOAD_KEY, true).toBool());
bool draw_axes = settings.value(DRAW_AXES_KEY, false).toBool();
orthographic_action->setChecked(true);
}
+ QString path = settings.value(OPEN_EXTERNAL_KEY, "").toString();
+ if (!QDir::isAbsolutePath(path) && !path.isEmpty())
+ {
+ path = QStandardPaths::findExecutable(path);
+ }
+ QString displayName = path.mid(path.lastIndexOf(QDir::separator()) + 1);
+ open_external_action->setText("Open with " + displayName);
+ open_external_action->setData(path);
+
DrawMode draw_mode = (DrawMode)settings.value(DRAW_MODE_KEY, DRAWMODECOUNT).toInt();
if(draw_mode >= DRAWMODECOUNT)
{
draw_mode = shaded;
}
- canvas->set_drawMode(draw_mode);
- QAction* (dm_acts[]) = {shaded_action, wireframe_action, surfaceangle_action};
+ QAction* (dm_acts[]) = {shaded_action, wireframe_action, surfaceangle_action, meshlight_action};
dm_acts[draw_mode]->setChecked(true);
+ on_drawMode(dm_acts[draw_mode]);
resize(600, 400);
restoreGeometry(settings.value(WINDOW_GEOM_KEY).toByteArray());
}
+void Window::on_drawModePrefs() {
+ // For now only one draw mode has settings
+ // when settings for other draw mode will be available
+ // we will need to check the current mode
+ if (meshlightprefs->isVisible()) {
+ meshlightprefs->hide();
+ } else {
+ meshlightprefs->show();
+ }
+}
+
void Window::on_open()
{
- QString filename = QFileDialog::getOpenFileName(
- this, "Load .stl file", QString(), "STL files (*.stl, *.STL)");
+ const QString filename = QFileDialog::getOpenFileName(
+ this, "Load .stl file", QString(), "STL files (*.stl *.STL)");
if (!filename.isNull())
{
load_stl(filename);
}
}
+void Window::on_open_external() const
+{
+ if (current_file.isEmpty())
+ {
+ return;
+ }
+
+
+ QString program = open_external_action->data().toString();
+ if (program.isEmpty()) {
+ program = QFileDialog::getOpenFileName((QWidget*) this, "Select program to open with", QDir::rootPath());
+ if (!program.isEmpty()) {
+ QSettings settings;
+ settings.setValue(OPEN_EXTERNAL_KEY, program);
+ QString displayName = program.mid(program.lastIndexOf(QDir::separator()) + 1);
+ open_external_action->setText("Open with " + displayName);
+ open_external_action->setData(program);
+ }
+ }
+
+ QProcess::startDetached(program, QStringList(current_file));
+}
+
void Window::on_about()
{
QMessageBox::about(this, "",
"<p>A fast viewer for <code>.stl</code> files.<br>"
"<a href=\"https://github.com/fstl-app/fstl\""
" style=\"color: #93a1a1;\">https://github.com/fstl-app/fstl</a></p>"
- "<p>© 2014-2022 Matthew Keeter<br>"
+ "<p>© 2014-2024 Matthew Keeter<br>"
"<a href=\"mailto:matt.j.keeter@gmail.com\""
" style=\"color: #93a1a1;\">matt.j.keeter@gmail.com</a></p>");
}
void Window::on_drawMode(QAction* act)
{
+ // On mode change hide prefs first
+ meshlightprefs->hide();
+
DrawMode mode;
if (act == shaded_action)
{
+ drawModePrefs_action->setEnabled(false);
mode = shaded;
}
else if (act == wireframe_action)
{
+ drawModePrefs_action->setEnabled(false);
mode = wireframe;
}
- else
+ else if (act == surfaceangle_action)
{
+ drawModePrefs_action->setEnabled(false);
mode = surfaceangle;
}
+ else if (act == meshlight_action)
+ {
+ drawModePrefs_action->setEnabled(true);
+ mode = meshlight;
+ }
canvas->set_drawMode(mode);
QSettings().setValue(DRAW_MODE_KEY, mode);
}
QSettings().setValue(INVERT_ZOOM_KEY, d);
}
+void Window::on_resetTransformOnLoad(bool d) {
+ canvas->setResetTransformOnLoad(d);
+ QSettings().setValue(RESET_TRANSFORM_ON_LOAD_KEY, d);
+}
+
void Window::on_watched_change(const QString& filename)
{
if (autoreload_action->isChecked())
}
}
+void Window::on_common_view_change(QAction* common)
+{
+ if (common == common_view_center_action) canvas->common_view_change(centerview);
+ if (common == common_view_iso_action) canvas->common_view_change(isoview);
+ if (common == common_view_top_action) canvas->common_view_change(topview);
+ if (common == common_view_bottom_action) canvas->common_view_change(bottomview);
+ if (common == common_view_left_action) canvas->common_view_change(leftview);
+ if (common == common_view_right_action) canvas->common_view_change(rightview);
+ if (common == common_view_front_action) canvas->common_view_change(frontview);
+ if (common == common_view_back_action) canvas->common_view_change(backview);
+}
+
bool Window::load_stl(const QString& filename, bool is_reload)
{
if (!open_action->isEnabled()) return false;
QMainWindow::keyPressEvent(event);
}
+
+void Window::on_fullscreen() {
+ if (!this->isFullScreen()) {
+ this->showFullScreen();
+ } else {
+ this->showNormal();
+ }
+}
#include <QCollator>
class Canvas;
+class ShaderLightPrefs;
class Window : public QMainWindow
{
public slots:
void on_open();
+ void on_open_external() const;
void on_about();
void on_bad_stl();
void on_empty_mesh();
void on_drawMode(QAction* mode);
void on_drawAxes(bool d);
void on_invertZoom(bool d);
+ void on_resetTransformOnLoad(bool d);
void on_watched_change(const QString& filename);
void on_reload();
+ void on_common_view_change(QAction* common);
void on_autoreload_triggered(bool r);
void on_clear_recent();
void on_load_recent(QAction* a);
void on_loaded(const QString& filename);
void on_save_screenshot();
+ void on_fullscreen();
void on_hide_menuBar();
+ void on_drawModePrefs();
private:
void rebuild_recent_files();
QPair<QString, QString> get_file_neighbors();
QAction* const open_action;
+ QAction* const open_external_action;
QAction* const about_action;
QAction* const quit_action;
QAction* const perspective_action;
+ QAction* const common_view_center_action;
+ QAction* const common_view_iso_action;
+ QAction* const common_view_top_action;
+ QAction* const common_view_bottom_action;
+ QAction* const common_view_left_action;
+ QAction* const common_view_right_action;
+ QAction* const common_view_front_action;
+ QAction* const common_view_back_action;
QAction* const orthographic_action;
QAction* const shaded_action;
QAction* const wireframe_action;
QAction* const surfaceangle_action;
+ QAction* const meshlight_action;
+ QAction* const drawModePrefs_action;
QAction* const axes_action;
QAction* const invert_zoom_action;
QAction* const reload_action;
QAction* const autoreload_action;
QAction* const save_screenshot_action;
QAction* const hide_menuBar_action;
+ QAction* const fullscreen_action;
+ QAction* const resetTransformOnLoadAction;
QMenu* const recent_files;
QActionGroup* const recent_files_group;
QAction* const recent_files_clear_action;
const static int MAX_RECENT_FILES=8;
+ static const QString OPEN_EXTERNAL_KEY;
const static QString RECENT_FILE_KEY;
const static QString INVERT_ZOOM_KEY;
const static QString AUTORELOAD_KEY;
const static QString PROJECTION_KEY;
const static QString DRAW_MODE_KEY;
const static QString WINDOW_GEOM_KEY;
+ const static QString RESET_TRANSFORM_ON_LOAD_KEY;
QString current_file;
QString lookup_folder;
QFileSystemWatcher* watcher;
Canvas* canvas;
+
+ ShaderLightPrefs* meshlightprefs;
};
#endif // WINDOW_H
--- /dev/null
+Linux :
+-----------
+desktop file and application icons installation.
+This tells the system that fstl knows to open stl files and allow stl to
+be launched using windows key.
+
+Install :
+./xdg_install.sh fstl
+
+Uninstall :
+./xdg_uninstall.sh fstl
+
+if runned as regular user this will install locally in :
+ $HOME/.local/share/mime/
+ $HOME/.local/share/applications/
+ $HOME/.local/share/icons/
+
+if runned as root this will install system-wide in :
+ /usr/share/mime
+ /usr/share/applications
+ /usr/share/icons
+
+Third script xdg_package_install.sh is to be used when building deb or rpm package.
--- /dev/null
+[Desktop Entry]
+Name=fstl
+GenericName=Fast STL Viewer
+Exec=fstl %U
+Terminal=false
+Icon=fstlapp-fstl
+Type=Application
+MimeType=model/stl;
+Categories=Utility;
--- /dev/null
+#!/bin/bash
+
+# This script will install mimetypes, icons and desktop file,
+# it takes a name in argument
+#
+# if runned as regular user this will install locally in :
+# $HOME/.local/share/mime/
+# $HOME/.local/share/applications/
+# $HOME/.local/share/icons/
+#
+# if runned as root this will install system-wide in :
+# /usr/share/mime
+# /usr/share/applications
+# /usr/share/icons
+
+if [ $# != 1 ]; then
+ echo "You must provide an application name"
+ exit 1
+fi
+
+name=$1
+
+# echo "Installing mimetypes"
+# xdg-mime install fstlapp-$name-mimetypes.xml
+
+echo "Installing desktop file"
+xdg-desktop-menu install fstlapp-$name.desktop
+
+echo "Installing apps icons"
+iclist="fstlapp-$name"
+for im in $iclist
+do
+ xdg-icon-resource install --theme hicolor --context apps --size 16 icons/${im}_16x16.png $im
+ xdg-icon-resource install --theme hicolor --context apps --size 22 icons/${im}_22x22.png $im
+ xdg-icon-resource install --theme hicolor --context apps --size 32 icons/${im}_32x32.png $im
+ xdg-icon-resource install --theme hicolor --context apps --size 48 icons/${im}_48x48.png $im
+ xdg-icon-resource install --theme hicolor --context apps --size 64 icons/${im}_64x64.png $im
+ xdg-icon-resource install --theme hicolor --context apps --size 128 icons/${im}_128x128.png $im
+ xdg-icon-resource install --theme hicolor --context apps --size 256 icons/${im}_256x256.png $im
+done
+
+# echo "Installing mimetypes icons"
+# iclist="`cat fstlapp-$name-mimetypes.xml | grep "icon name" | sed 's/^.*"\(.*\)".*$/\1/'`"
+# for im in $iclist
+# do
+# xdg-icon-resource install --theme hicolor --context mimetypes --size 16 icons/${im}_16x16.png $im
+# xdg-icon-resource install --theme hicolor --context mimetypes --size 22 icons/${im}_22x22.png $im
+# xdg-icon-resource install --theme hicolor --context mimetypes --size 32 icons/${im}_32x32.png $im
+# xdg-icon-resource install --theme hicolor --context mimetypes --size 48 icons/${im}_48x48.png $im
+# xdg-icon-resource install --theme hicolor --context mimetypes --size 64 icons/${im}_64x64.png $im
+# done
+
--- /dev/null
+#!/bin/bash
+
+# For a package installation (rpm or deb), we must proceed a different way
+# This script takes two arguments, the first one is the installation
+# prefix and the second is the name
+
+if [ $# != 2 ]; then
+ echo "You must provide two arguments"
+ exit 1
+fi
+
+base=$1
+name=$2
+
+# echo "Drop mimetypes file in /usr/share/mime/packages/"
+# mkdir -p $base/usr/share/mime/packages/
+# cp fstlapp-$name-mimetypes.xml $base/usr/share/mime/packages/
+
+echo "Drop desktop file in /usr/share/applications/"
+mkdir -p $base/usr/share/applications/
+cp fstlapp-$name.desktop $base/usr/share/applications/
+
+slist="16 22 32 48 64 128 256"
+echo "Installing apps icons"
+iclist="fstlapp-$name"
+for im in $iclist
+do
+ for s in $slist
+ do
+ mkdir -p $base/usr/share/icons/hicolor/${s}x${s}/apps
+ cp icons/${im}_${s}x${s}.png $base/usr/share/icons/hicolor/${s}x${s}/apps/$im.png
+ done
+done
+
+# echo "Installing mimetypes icons"
+# iclist="`cat fstlapp-$name-mimetypes.xml | grep "icon name" | sed 's/^.*"\(.*\)".*$/\1/'`"
+# for im in $iclist
+# do
+# for s in $slist
+# do
+# mkdir -p $base/usr/share/icons/hicolor/${s}x${s}/mimetypes
+# cp icons/${im}_${s}x${s}.png $base/usr/share/icons/hicolor/${s}x${s}/mimetypes/$im.png
+# done
+# done
+
+#
+# Put this in the post installation and post uninstallation scripts
+#
+#echo "Updating mime database"
+#update-mime-database /usr/share/mim
+#
+#echo "Updating desktop database"
+#update-desktop-database
--- /dev/null
+#!/bin/bash
+
+# This script will uninstall mimetypes, icons and desktop file
+#
+# if runned as regular user this will uninstall locally from :
+# $HOME/.local/share/mime/
+# $HOME/.local/share/applications/
+# $HOME/.local/share/icons/
+#
+# if runned as root this will uninstall system-wide from :
+# /usr/share/mime
+# /usr/share/applications
+# /usr/share/icons
+
+if [ $# != 1 ]; then
+ echo "You must provide a name"
+ exit 1
+fi
+
+name=$1
+
+# echo "Uninstalling mimetypes"
+# xdg-mime uninstall fstlapp-$name-mimetypes.xml
+
+echo "Uninstalling desktop file"
+xdg-desktop-menu uninstall fstlapp-$name.desktop
+
+echo "Uninstalling apps icons"
+iclist="fstlapp-$name"
+for im in $iclist
+do
+ xdg-icon-resource uninstall --theme hicolor --context apps --size 16 $im
+ xdg-icon-resource uninstall --theme hicolor --context apps --size 22 $im
+ xdg-icon-resource uninstall --theme hicolor --context apps --size 32 $im
+ xdg-icon-resource uninstall --theme hicolor --context apps --size 48 $im
+ xdg-icon-resource uninstall --theme hicolor --context apps --size 64 $im
+ xdg-icon-resource uninstall --theme hicolor --context apps --size 128 $im
+ xdg-icon-resource uninstall --theme hicolor --context apps --size 256 $im
+done
+
+# echo "Uninstalling mimetypes icons"
+# iclist="`cat fstlapp-$name-mimetypes.xml | grep "icon name" | sed 's/^.*"\(.*\)".*$/\1/'`"
+# for im in $iclist
+# do
+# xdg-icon-resource uninstall --theme hicolor --context mimetypes --size 16 $im
+# xdg-icon-resource uninstall --theme hicolor --context mimetypes --size 22 $im
+# xdg-icon-resource uninstall --theme hicolor --context mimetypes --size 32 $im
+# xdg-icon-resource uninstall --theme hicolor --context mimetypes --size 48 $im
+# xdg-icon-resource uninstall --theme hicolor --context mimetypes --size 64 $im
+# done