introduction tour

This commit is contained in:
Sampo Niskanen 2011-12-24 18:03:56 +00:00
parent a10bfbe72d
commit 9e04fd5667
33 changed files with 311 additions and 96 deletions

View File

@ -0,0 +1,30 @@
#!/bin/bash
#
# A script to batch-convert all source image files into suitable
# slideset image files. It converts all *.png, *.jpg and *.xcf.gz
# files into the suitably sized jpg images in the datafiles directory.
#
DEST=../../datafiles/tours
CONVERSION="-background #ececec -flatten -geometry 600x400 -quality 85"
# Convert all xcf files
find -iname "*.xcf.gz" | grep -v MANUAL | while read FILE; do
echo Converting $FILE
BASE="$(echo $FILE | sed 's/\.xcf\.gz$//')"
xcf2png "$FILE" | convert $CONVERSION - $DEST/$BASE.jpg
done
# Convert all png and jpg files
find -iname "*.png" -o -iname "*.jpg" | grep -v MANUAL | while read FILE; do
echo Converting $FILE
BASE="$(echo $FILE | sed 's/\.png$//' | sed 's/\.jpg$//')"
convert $CONVERSION $FILE $DEST/$BASE.jpg
done

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -0,0 +1,84 @@
Introduction
<p>This first tour provides a quick overview of OpenRocket screens and
features.
[logo.png]
# TODO: Add "Welcome to OpenRocket" text to image.
<p><b>OpenRocket</b> is a versatile application for designing,
simulating and optimizing model rockets. This first tour provides an
overview of the OpenRocket screens and features.
<p>You can browse through the tour using the <b>Next</b> and
<b>Previous</b> buttons, or using the <em>left</em> and <em>right
arrows</em> on your keyboard.
[none]
<p>This is the startup screen from which you can create a new rocket
design or open existing designs.
<p>For this tour, let's open an example design called <em>A simple model
rocket</em>.
[main_window.jpg]
<p>This is the main screen of OpenRocket. It is divided horizontally
into two parts: the rocket design / flight simulation section and the
rocket design view.
[main_window_top.jpg]
<p>On the top left is the component tree of the rocket design.
It displays which components are attached to what components.
<p>Next to it are buttons which allow adding new components to the
rocket.
[main_window_bottom.jpg]
<p>The bottom half of the window contains a diagram of the current
rocket design.
<p>The different viewing options are described in detail in the
<a href="FIXME">Viewing options</a> tour.
[flight_simulations.jpg]
<p>When you select the <b>Flight simulations</b> tab, the top part of
the window changes to show the simulations that have been defined for
the rocket.
<p>You can define various simulations with different motor
configurations and differing launch conditions, such as wind and
launch rod angle.
[simulation_edit.png]
<p>By double-clicking on a simulation you open the <em>Simulation edit
dialog</em>. On the first two tabs you can define simulation options,
on the last two tabs you can plot or export the results.
<p>Simulating a rocket is described in detail in the
<a href="FIXME">Simulating a flight</a> tour.
[advanced_features.jpg]
<p>Other advanced features include component analysis and automatic
design optimization, which are covered by separate tours.
<p>Next you can take a tour on <a href="FIXME">Creating a rocket
design</a>, browse other tours or start experimenting with the
software.

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@ -11,10 +11,14 @@ div.base {
}
p {
margin: 0 0 5px;
margin: 0 0 8px;
}
a {
color: #0000dd;
text-decoration: underline;
}
em {
font-style: italics;
}

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -11,7 +11,8 @@ This is the <i>description</i>.
<p>This is the first slide &mdash; the left_design file.
<p>It's nifty. It can also contain <a href="foobar">links</a> (though they don't work yet).
<p>Tour2: <a href="test2/test2.tour">Test2</a>
<p>Hyperlink: <a href="http://www.google.com">google</a>
[main_window.png]

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -1,6 +1,9 @@
# This file lists all the available tours.
test.tour
test2.tour
introduction/introduction.tour
test1/test.tour
test2/test2.tour

View File

@ -5,8 +5,6 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import javax.swing.AbstractListModel;
@ -29,16 +27,14 @@ import net.sf.openrocket.gui.components.StyledLabel.Style;
import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Named;
public class GuidedTourSelectionDialog extends JDialog {
private static final Translator trans = Application.getTranslator();
private static final String TOURS_BASE_DIR = "datafiles/tours";
private final SlideSetManager slideSetManager;
private final List<String> tourNames;
@ -52,21 +48,9 @@ public class GuidedTourSelectionDialog extends JDialog {
public GuidedTourSelectionDialog(Window parent) {
super(parent, trans.get("title"), ModalityType.MODELESS);
try {
slideSetManager = new SlideSetManager(TOURS_BASE_DIR);
slideSetManager.load();
tourNames = slideSetManager.getSlideSetNames();
if (tourNames.isEmpty()) {
throw new FileNotFoundException("No tours found.");
}
} catch (IOException e) {
throw new BugException(e);
}
slideSetManager = SlideSetManager.getSlideSetManager();
tourNames = slideSetManager.getSlideSetNames();
JPanel panel = new JPanel(new MigLayout("fill"));
panel.add(new StyledLabel(trans.get("lbl.selectTour"), Style.BOLD), "spanx, wrap rel");
@ -87,10 +71,10 @@ public class GuidedTourSelectionDialog extends JDialog {
}
}
});
panel.add(new JScrollPane(tourList), "grow, gapright unrel, w 200lp, h 150lp");
panel.add(new JScrollPane(tourList), "grow, gapright unrel, w 200lp, h 250lp");
// Sub-panel containing description and start button
JPanel sub = new JPanel(new MigLayout("fill, ins 0"));
sub.add(new StyledLabel(trans.get("lbl.description"), -1), "wrap rel");
@ -113,10 +97,10 @@ public class GuidedTourSelectionDialog extends JDialog {
});
sub.add(start, "growx");
panel.add(sub, "grow, wrap para, w 200lp, h 150lp");
panel.add(sub, "grow, wrap para, w 350lp, h 250lp");
JButton close = new JButton(trans.get("button.close"));
close.addActionListener(new ActionListener() {
@Override
@ -128,6 +112,7 @@ public class GuidedTourSelectionDialog extends JDialog {
this.add(panel);
GUIUtil.setDisposableDialogOptions(this, close);
GUIUtil.rememberWindowPosition(this);
tourList.setSelectedIndex(0);
}
@ -192,6 +177,6 @@ public class GuidedTourSelectionDialog extends JDialog {
}
}

View File

@ -14,6 +14,7 @@ import javax.imageio.ImageIO;
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class Slide {
private static final String NO_IMAGE = "none";
private final String imageFile;
private SoftReference<BufferedImage> imageReference = null;
@ -21,16 +22,20 @@ public class Slide {
private final String text;
public Slide(String imageFile, String text) {
this.imageFile = imageFile;
this.text = text;
}
public BufferedImage getImage() {
if (imageFile.equals(NO_IMAGE)) {
return new BufferedImage(0, 0, BufferedImage.TYPE_INT_ARGB);
}
// Check the cache
if (imageReference != null) {
BufferedImage image = imageReference.get();
@ -51,7 +56,7 @@ public class Slide {
}
private BufferedImage loadImage() {
BufferedImage img;

View File

@ -11,6 +11,8 @@ import java.util.Map;
import javax.swing.text.html.StyleSheet;
import net.sf.openrocket.util.BugException;
/**
* A manager that loads a number of slide sets from a defined base directory
* and provides access to them.
@ -18,11 +20,14 @@ import javax.swing.text.html.StyleSheet;
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class SlideSetManager {
private static final String TOURS_BASE_DIR = "datafiles/tours";
private static final String TOURS_FILE = "tours.txt";
private static final String STYLESHEET_FILE = "style.css";
private static SlideSetManager slideSetManager = null;
private final String baseDir;
private final Map<String, SlideSet> slideSets = new LinkedHashMap<String, SlideSet>();
@ -49,20 +54,24 @@ public class SlideSetManager {
List<String> tours = loadTourList();
StyleSheet styleSheet = loadStyleSheet();
for (String file : tours) {
for (String fileAndDir : tours) {
String base;
String file;
String base = baseDir + file;
int index = base.lastIndexOf('/');
String fullFileAndDir = baseDir + fileAndDir;
int index = fullFileAndDir.lastIndexOf('/');
if (index >= 0) {
base = base.substring(0, index);
base = fullFileAndDir.substring(0, index);
file = fullFileAndDir.substring(index + 1);
} else {
base = "";
file = "";
}
SlideSetLoader loader = new SlideSetLoader(base);
SlideSet set = loader.load(file);
set.setStyleSheet(styleSheet);
slideSets.put(file, set);
slideSets.put(fileAndDir, set);
}
}
@ -131,4 +140,26 @@ public class SlideSetManager {
}
/**
* Return a singleton implementation that has loaded the default tours.
*/
public static SlideSetManager getSlideSetManager() {
if (slideSetManager == null) {
try {
SlideSetManager ssm = new SlideSetManager(TOURS_BASE_DIR);
ssm.load();
if (ssm.getSlideSetNames().isEmpty()) {
throw new FileNotFoundException("No tours found.");
}
slideSetManager = ssm;
} catch (IOException e) {
throw new BugException(e);
}
}
return slideSetManager;
}
}

View File

@ -19,6 +19,10 @@ import net.sf.openrocket.gui.components.ImageDisplayComponent;
*/
public class SlideShowComponent extends JSplitPane {
private final int WIDTH = 600;
private final int HEIGHT_IMAGE = 400;
private final int HEIGHT_TEXT = 100;
private final ImageDisplayComponent imageDisplay;
private final JEditorPane textPane;
@ -27,21 +31,21 @@ public class SlideShowComponent extends JSplitPane {
super(VERTICAL_SPLIT);
imageDisplay = new ImageDisplayComponent();
imageDisplay.setPreferredSize(new Dimension(600, 350));
imageDisplay.setPreferredSize(new Dimension(WIDTH, HEIGHT_IMAGE));
this.setLeftComponent(imageDisplay);
textPane = new JEditorPane("text/html", "");
textPane.setEditable(false);
textPane.setPreferredSize(new Dimension(600, 100));
textPane.setPreferredSize(new Dimension(WIDTH, HEIGHT_TEXT));
JScrollPane scrollPanel = new JScrollPane(textPane);
this.setRightComponent(scrollPanel);
this.setResizeWeight(0.7);
this.setResizeWeight(((double) HEIGHT_IMAGE) / (HEIGHT_IMAGE + HEIGHT_TEXT));
}
public void setSlide(Slide slide) {
this.imageDisplay.setImage(slide.getImage());
this.textPane.setText(slide.getText());

View File

@ -3,24 +3,28 @@ package net.sf.openrocket.gui.help.tours;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Locale;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.JRootPane;
import javax.swing.KeyStroke;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Chars;
public class SlideShowDialog extends JDialog {
private static final LogHelper log = Application.getLogger();
private static final Translator trans = Application.getTranslator();
private SlideShowComponent slideShowComponent;
@ -38,35 +42,38 @@ public class SlideShowDialog extends JDialog {
JPanel panel = new JPanel(new MigLayout("fill"));
slideShowComponent = new SlideShowComponent();
slideShowComponent.addHyperlinkListener(new SlideShowLinkListener(parent));
panel.add(slideShowComponent, "spanx, grow, wrap para");
JPanel sub = new JPanel(new MigLayout("ins 0, fill"));
prevButton = new JButton(Chars.LEFT_ARROW + " " + trans.get("btn.prev"));
prevButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
log.user("Clicked previous button");
setPosition(position - 1);
}
});
sub.add(prevButton, "left");
nextButton = new JButton(trans.get("btn.next") + " " + Chars.RIGHT_ARROW);
nextButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
log.user("Clicked next button");
setPosition(position + 1);
}
});
sub.add(nextButton, "left, gapleft para");
sub.add(new JPanel(), "growx");
closeButton = new JButton(trans.get("button.close"));
closeButton.addActionListener(new ActionListener() {
@Override
@ -76,12 +83,16 @@ public class SlideShowDialog extends JDialog {
});
sub.add(closeButton, "right");
panel.add(sub, "growx");
this.add(panel);
updateEnabled();
addKeyActions();
GUIUtil.setDisposableDialogOptions(this, nextButton);
nextButton.grabFocus();
GUIUtil.rememberWindowPosition(this);
GUIUtil.rememberWindowSize(this);
this.setAlwaysOnTop(true);
}
@ -120,41 +131,43 @@ public class SlideShowDialog extends JDialog {
}
public static void main(String[] args) throws Exception {
Locale.setDefault(new Locale("de", "DE", ""));
SlideSetManager manager = new SlideSetManager("datafiles/tours");
manager.load();
final SlideSet set = manager.getSlideSet("test.tour");
SwingUtilities.invokeAndWait(new Runnable() {
private void addKeyActions() {
Action next = new AbstractAction() {
@Override
public void run() {
SlideShowDialog ssd = new SlideShowDialog(null);
ssd.slideShowComponent.addHyperlinkListener(new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
System.out.println("Hyperlink event: " + e);
System.out.println("Event type: " + e.getEventType());
System.out.println("Description: " + e.getDescription());
System.out.println("URL: " + e.getURL());
System.out.println("Source element: " + e.getSourceElement());
}
});
ssd.setSize(500, 500);
ssd.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
ssd.setVisible(true);
ssd.setSlideSet(set, 0);
public void actionPerformed(ActionEvent event) {
log.user("Key action for next slide");
if (position < slideSet.getSlideCount() - 1) {
setPosition(position + 1);
}
}
});
};
Action previous = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent event) {
log.user("Key action for previous slide");
if (position > 0) {
setPosition(position - 1);
}
}
};
String nextKey = "slide:next";
String prevKey = "slide:previous";
JRootPane root = this.getRootPane();
root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), nextKey);
root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_KP_RIGHT, 0), nextKey);
root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), prevKey);
root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, 0), prevKey);
root.getActionMap().put(nextKey, next);
root.getActionMap().put(prevKey, previous);
}
}

View File

@ -0,0 +1,55 @@
package net.sf.openrocket.gui.help.tours;
import java.awt.Desktop;
import java.awt.Window;
import java.net.URL;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkEvent.EventType;
import javax.swing.event.HyperlinkListener;
import net.sf.openrocket.startup.Application;
public class SlideShowLinkListener implements HyperlinkListener {
private final Window parent;
public SlideShowLinkListener(Window parent) {
this.parent = parent;
}
@Override
public void hyperlinkUpdate(HyperlinkEvent event) {
if (event.getEventType() != EventType.ACTIVATED) {
return;
}
URL url = event.getURL();
if (url != null && (url.getProtocol().equalsIgnoreCase("http") || url.getProtocol().equals("https"))) {
if (Desktop.isDesktopSupported()) {
try {
Desktop.getDesktop().browse(url.toURI());
} catch (Exception e) {
// Ignore
}
}
} else {
String name = event.getDescription();
try {
SlideSet ss = SlideSetManager.getSlideSetManager().getSlideSet(name);
SlideShowDialog dialog = new SlideShowDialog(parent);
dialog.setSlideSet(ss, 0);
dialog.setVisible(true);
} catch (IllegalArgumentException e) {
Application.getExceptionHandler().handleErrorCondition("Guided tour '" + name + "' not found.");
}
}
}
}

View File

@ -127,8 +127,8 @@ public class GUIUtil {
}
/**
* Set suitable options for a single-use disposable dialog. This includes
* setting ESC to close the dialog, adding the appropriate window icons and
@ -153,7 +153,7 @@ public class GUIUtil {
}
/**
* Add the correct action to close a JDialog when the ESC key is pressed.
* The dialog is closed by sending is a WINDOW_CLOSING event.
@ -192,7 +192,7 @@ public class GUIUtil {
}
/**
* Change the behavior of a component so that TAB and Shift-TAB cycles the focus of
* the components. This is necessary for e.g. <code>JTextArea</code>.
@ -207,7 +207,7 @@ public class GUIUtil {
}
/**
* Set the OpenRocket icons to the window icons.
*
@ -236,7 +236,7 @@ public class GUIUtil {
}
/**
* Set the best available look-and-feel into use.
*/
@ -294,7 +294,7 @@ public class GUIUtil {
}
/**
* Automatically remember the size of a window. This stores the window size in the user
* preferences when resizing/maximizing the window and sets the state on the first call.
@ -377,7 +377,7 @@ public class GUIUtil {
}
/**
* Traverses recursively the component tree, and sets all applicable component
* models to null, so as to remove the listener connections. After calling this
@ -519,7 +519,7 @@ public class GUIUtil {
}
/**
* A mouse listener that toggles the state of a boolean value in a table model
* when clicked on another column of the table.