summaryrefslogtreecommitdiffstats
path: root/doc/html/tutorial2-05.html
diff options
context:
space:
mode:
authorTimothy Pearson <kb9vqf@pearsoncomputing.net>2011-07-10 15:24:15 -0500
committerTimothy Pearson <kb9vqf@pearsoncomputing.net>2011-07-10 15:24:15 -0500
commitbd0f3345a938b35ce6a12f6150373b0955b8dd12 (patch)
tree7a520322212d48ebcb9fbe1087e7fca28b76185c /doc/html/tutorial2-05.html
downloadqt3-bd0f3345a938b35ce6a12f6150373b0955b8dd12.tar.gz
qt3-bd0f3345a938b35ce6a12f6150373b0955b8dd12.zip
Add Qt3 development HEAD version
Diffstat (limited to 'doc/html/tutorial2-05.html')
-rw-r--r--doc/html/tutorial2-05.html587
1 files changed, 587 insertions, 0 deletions
diff --git a/doc/html/tutorial2-05.html b/doc/html/tutorial2-05.html
new file mode 100644
index 0000000..ca724c0
--- /dev/null
+++ b/doc/html/tutorial2-05.html
@@ -0,0 +1,587 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!-- /home/espenr/tmp/qt-3.3.8-espenr-2499/qt-x11-free-3.3.8/doc/tutorial2.doc:349 -->
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>Presenting the GUI</title>
+<style type="text/css"><!--
+fn { margin-left: 1cm; text-indent: -1cm; }
+a:link { color: #004faf; text-decoration: none }
+a:visited { color: #672967; text-decoration: none }
+body { background: #ffffff; color: black; }
+--></style>
+</head>
+<body>
+
+<table border="0" cellpadding="0" cellspacing="0" width="100%">
+<tr bgcolor="#E5E5E5">
+<td valign=center>
+ <a href="index.html">
+<font color="#004faf">Home</font></a>
+ | <a href="classes.html">
+<font color="#004faf">All&nbsp;Classes</font></a>
+ | <a href="mainclasses.html">
+<font color="#004faf">Main&nbsp;Classes</font></a>
+ | <a href="annotated.html">
+<font color="#004faf">Annotated</font></a>
+ | <a href="groups.html">
+<font color="#004faf">Grouped&nbsp;Classes</font></a>
+ | <a href="functions.html">
+<font color="#004faf">Functions</font></a>
+</td>
+<td align="right" valign="center"><img src="logo32.png" align="right" width="64" height="32" border="0"></td></tr></table><h1 align=center>Presenting the GUI</h1>
+
+
+<p>
+<p> <center><img src="chart-main2.png" alt="The chart application"></center>
+<p> The <tt>chart</tt> application provides access to options via menus and
+toolbar buttons arranged around a central widget, a CanvasView, in a
+conventional document-centric style.
+<p> (Extracts from <tt>chartform.h</tt>.)
+<p>
+
+<pre> class ChartForm: public <a href="qmainwindow.html">QMainWindow</a>
+ {
+ <a href="metaobjects.html#Q_OBJECT">Q_OBJECT</a>
+ public:
+ enum { MAX_ELEMENTS = 100 };
+ enum { MAX_RECENTFILES = 9 }; // Must not exceed 9
+ enum ChartType { PIE, VERTICAL_BAR, HORIZONTAL_BAR };
+ enum AddValuesType { NO, YES, AS_PERCENTAGE };
+
+ ChartForm( const <a href="qstring.html">QString</a>&amp; filename );
+ ~ChartForm();
+
+ int chartType() { return m_chartType; }
+ void setChanged( bool changed = TRUE ) { m_changed = changed; }
+ void drawElements();
+
+ <a href="qpopupmenu.html">QPopupMenu</a> *optionsMenu; // Why public? See canvasview.cpp
+
+ protected:
+ virtual void closeEvent( <a href="qcloseevent.html">QCloseEvent</a> * );
+
+ private slots:
+ void fileNew();
+ void fileOpen();
+ void fileOpenRecent( int index );
+ void fileSave();
+ void fileSaveAs();
+ void fileSaveAsPixmap();
+ void filePrint();
+ void fileQuit();
+ void optionsSetData();
+ void updateChartType( <a href="qaction.html">QAction</a> *action );
+ void optionsSetFont();
+ void optionsSetOptions();
+ void helpHelp();
+ void helpAbout();
+ void helpAboutQt();
+ void saveOptions();
+
+ private:
+ void init();
+ void load( const <a href="qstring.html">QString</a>&amp; filename );
+ bool okToClear();
+ void drawPieChart( const double scales[], double total, int count );
+ void drawVerticalBarChart( const double scales[], double total, int count );
+ void drawHorizontalBarChart( const double scales[], double total, int count );
+
+ <a href="qstring.html">QString</a> valueLabel( const <a href="qstring.html">QString</a>&amp; label, double value, double total );
+ void updateRecentFiles( const <a href="qstring.html">QString</a>&amp; filename );
+ void updateRecentFilesMenu();
+ void setChartType( ChartType chartType );
+
+ <a href="qpopupmenu.html">QPopupMenu</a> *fileMenu;
+ <a href="qaction.html">QAction</a> *optionsPieChartAction;
+ <a href="qaction.html">QAction</a> *optionsHorizontalBarChartAction;
+ <a href="qaction.html">QAction</a> *optionsVerticalBarChartAction;
+ <a href="qstring.html">QString</a> m_filename;
+ <a href="qstringlist.html">QStringList</a> m_recentFiles;
+ <a href="qcanvas.html">QCanvas</a> *m_canvas;
+ CanvasView *m_canvasView;
+ bool m_changed;
+ ElementVector m_elements;
+ <a href="qprinter.html">QPrinter</a> *m_printer;
+ ChartType m_chartType;
+ AddValuesType m_addValues;
+ int m_decimalPlaces;
+ <a href="qfont.html">QFont</a> m_font;
+ };
+</pre>
+<p> We create a <tt>ChartForm</tt> subclass of <a href="qmainwindow.html">QMainWindow</a>. Our subclass uses
+the <a href="metaobjects.html#Q_OBJECT">Q_OBJECT</a> macro to support Qt's <a href="signalsandslots.html">signals and slots</a> mechanism.
+<p> The public interface is very small; the type of chart being displayed
+can be retrieved, the chart can be marked 'changed' (so that the user
+will be prompted to save on exit), and the chart can be asked to draw
+itself (drawElements()). We've also made the options menu public
+because we are also going to use this menu as the canvas view's
+context menu.
+<p> <center><table cellpadding="4" cellspacing="2" border="0">
+<tr bgcolor="#f0f0f0">
+<td valign="top">The <a href="qcanvas.html">QCanvas</a> class is used for drawing 2D vector graphics. The
+<a href="qcanvasview.html">QCanvasView</a> class is used to present a view of a canvas in an
+application's GUI. All our drawing operations take place on the
+canvas; but events (e.g. mouse clicks) take place on the canvas view.
+</table></center>
+<p> Each action is represented by a private slot, e.g. <tt>fileNew()</tt>, <tt>optionsSetData()</tt>, etc. We also have quite a number of private
+functions and data members; we'll look at all these as we go through
+the implementation.
+<p> For the sake of convenience and compilation speed the chart form's
+implementation is split over three files, <tt>chartform.cpp</tt> for the
+GUI, <tt>chartform_canvas.cpp</tt> for the canvas handling and <tt>chartform_files.cpp</tt> for the file handling. We'll review each in turn.
+<p> <h2> The Chart Form GUI
+</h2>
+<a name="1"></a><p> (Extracts from <tt>chartform.cpp</tt>.)
+<p>
+
+<pre> #include "images/file_new.xpm"
+ #include "images/file_open.xpm"
+</pre><pre> #include "images/options_piechart.xpm"
+</pre>
+<p> All the images used by <tt>chart</tt> have been created as <tt>.xpm</tt> files
+which we've placed in the <tt>images</tt> subdirectory.
+<p> <h2> The Constructor
+</h2>
+<a name="2"></a><p> <pre> ChartForm::ChartForm( const <a href="qstring.html">QString</a>&amp; filename )
+ : <a href="qmainwindow.html">QMainWindow</a>( 0, 0, WDestructiveClose )
+</pre><tt>...</tt>
+<pre> <a href="qaction.html">QAction</a> *fileNewAction;
+ <a href="qaction.html">QAction</a> *fileOpenAction;
+ <a href="qaction.html">QAction</a> *fileSaveAction;
+</pre>
+<p> For each user action we declare a <a href="qaction.html">QAction</a> pointer. Some actions are
+declared in the header file because they need to be referred to
+outside of the constructor.
+<p> <center><table cellpadding="4" cellspacing="2" border="0">
+<tr bgcolor="#d0d0d0">
+<td valign="top">Most user actions are suitable as both menu items and as toolbar
+buttons. Qt allows us to create a single QAction which can be added to
+both a menu and a toolbar. This approach ensures that menu items and
+toolbar buttons stay in sync and saves duplicating code.
+</table></center>
+<p> <pre> fileNewAction = new <a href="qaction.html">QAction</a>(
+ "New Chart", QPixmap( file_new ),
+ "&amp;New", CTRL+Key_N, this, "new" );
+ <a href="qobject.html#connect">connect</a>( fileNewAction, SIGNAL( <a href="qaction.html#activated">activated</a>() ), this, SLOT( fileNew() ) );
+</pre>
+<p> When we construct an action we give it a name, an optional icon, a
+menu text, and an accelerator short-cut key (or 0 if no accelerator is
+required). We also make it a child of the form (by passing <tt>this</tt>).
+When the user clicks a toolbar button or clicks a menu option the <tt>activated()</tt> signal is emitted. We connect() this signal to the
+action's slot, in the snippet shown above, to fileNew().
+<p> The chart types are all mutually exclusive: you can have a pie chart
+<em>or</em> a vertical bar chart <em>or</em> a horizontal bar chart. This means
+that if the user selects the pie chart menu option, the pie chart
+toolbar button must be automatically selected too, and the other chart
+menu options and toolbar buttons must be automatically unselected.
+This behaviour is achieved by creating a <a href="qactiongroup.html">QActionGroup</a> and placing the
+chart type actions in the group.
+<p> <pre> <a href="qactiongroup.html">QActionGroup</a> *chartGroup = new <a href="qactiongroup.html">QActionGroup</a>( this ); // Connected later
+ chartGroup-&gt;<a href="qactiongroup.html#setExclusive">setExclusive</a>( TRUE );
+</pre>
+<p> The action group becomes a child of the form (<tt>this</tt>) and the
+exlusive behaviour is achieved by the setExclusive() call.
+<p> <pre> optionsPieChartAction = new <a href="qaction.html">QAction</a>(
+ "Pie Chart", QPixmap( options_piechart ),
+ "&amp;Pie Chart", CTRL+Key_I, chartGroup, "pie chart" );
+ optionsPieChartAction-&gt;<a href="qaction.html#setToggleAction">setToggleAction</a>( TRUE );
+</pre>
+<p> Each action in the group is created in the same way as other actions,
+except that the action's parent is the group rather than the form.
+Because our chart type actions have an on/off state we call
+setToggleAction(TRUE) for each of them. Note that we do not connect
+the actions; instead, later on, we will connect the group to a slot
+that will cause the canvas to redraw.
+<p> <center><table cellpadding="4" cellspacing="2" border="0">
+<tr bgcolor="#f0f0f0">
+<td valign="top">Why haven't we connected the group straight away? Later in the
+constructor we will read the user's options, one of which is the chart
+type. We will then set the chart type accordingly. But at that point
+we still won't have created a canvas or have any data, so all we want
+to do is toggle the canvas type toolbar buttons, but not actually draw
+the (at this point non-existent) canvas. <em>After</em> we have set the
+canvas type we will connect the group.
+</table></center>
+<p> Once we've created all our user actions we can create the toolbars and
+menu options that will allow the user to invoke them.
+<p> <pre> <a href="qtoolbar.html">QToolBar</a>* fileTools = new <a href="qtoolbar.html">QToolBar</a>( this, "file operations" );
+ fileTools-&gt;<a href="qtoolbar.html#setLabel">setLabel</a>( "File Operations" );
+ fileNewAction-&gt;<a href="qaction.html#addTo">addTo</a>( fileTools );
+ fileOpenAction-&gt;<a href="qaction.html#addTo">addTo</a>( fileTools );
+ fileSaveAction-&gt;<a href="qaction.html#addTo">addTo</a>( fileTools );
+</pre><tt>...</tt>
+<pre> fileMenu = new <a href="qpopupmenu.html">QPopupMenu</a>( this );
+ <a href="qmainwindow.html#menuBar">menuBar</a>()-&gt;insertItem( "&amp;File", fileMenu );
+ fileNewAction-&gt;<a href="qaction.html#addTo">addTo</a>( fileMenu );
+ fileOpenAction-&gt;<a href="qaction.html#addTo">addTo</a>( fileMenu );
+ fileSaveAction-&gt;<a href="qaction.html#addTo">addTo</a>( fileMenu );
+</pre>
+<p> Toolbar actions and menu options are easily created from QActions.
+<p> As a convenience to our users we will restore the last window position
+and size and list their recently used files. This is achieved by
+writing out their settings when the application is closed and reading
+them back when we construct the form.
+<p> <pre> <a href="qsettings.html">QSettings</a> settings;
+ settings.<a href="qsettings.html#insertSearchPath">insertSearchPath</a>( QSettings::Windows, WINDOWS_REGISTRY );
+ int windowWidth = settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "WindowWidth", 460 );
+ int windowHeight = settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "WindowHeight", 530 );
+ int windowX = settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "WindowX", -1 );
+ int windowY = settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "WindowY", -1 );
+ setChartType( ChartType(
+ settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "ChartType", int(PIE) ) ) );
+</pre><pre> m_font = QFont( "Helvetica", 18, QFont::Bold );
+ m_font.fromString(
+ settings.<a href="qsettings.html#readEntry">readEntry</a>( APP_KEY + "Font", m_font.toString() ) );
+ for ( int i = 0; i &lt; MAX_RECENTFILES; ++i ) {
+ <a href="qstring.html">QString</a> filename = settings.<a href="qsettings.html#readEntry">readEntry</a>( APP_KEY + "File" +
+ QString::<a href="qstring.html#number">number</a>( i + 1 ) );
+ if ( !filename.<a href="qstring.html#isEmpty">isEmpty</a>() )
+ m_recentFiles.push_back( filename );
+ }
+ if ( m_recentFiles.count() )
+ updateRecentFilesMenu();
+</pre>
+<p> The <a href="qsettings.html">QSettings</a> class handles user settings in a platform-independent
+way. We simply read and write settings, leaving QSettings to handle
+the platform dependencies. The insertSearchPath() call does nothing
+except under Windows so does not have to be <tt>#ifdef</tt>ed.
+<p> We use readNumEntry() calls to obtain the chart form's last size and
+position, providing default values if this is the first time it has
+been run. The chart type is retrieved as an integer and cast to a
+ChartType enum value. We create a default label font and then read the
+"Font" setting, using the default we have just created if necessary.
+<p> Although QSettings can handle string lists we've chosen to store each
+recently used file as a separate entry to make it easier to hand edit
+the settings. We attempt to read each possible file entry ("File1" to
+"File9"), and add each non-empty entry to the list of recently used
+files. If there are one or more recently used files we update the File
+menu by calling updateRecentFilesMenu(); (we'll review this later on).
+<p> <pre> <a href="qobject.html#connect">connect</a>( chartGroup, SIGNAL( <a href="qactiongroup.html#selected">selected</a>(QAction*) ),
+ this, SLOT( updateChartType(QAction*) ) );
+</pre>
+<p> Now that we have set the chart type (when we read it in as a user
+setting) it is safe to connect the chart group to our
+updateChartType() slot.
+<p> <pre> <a href="qwidget.html#resize">resize</a>( windowWidth, windowHeight );
+ if ( windowX != -1 || windowY != -1 )
+ <a href="qwidget.html#move">move</a>( windowX, windowY );
+</pre>
+<p> And now that we know the window size and position we can resize and
+move the chart form's window accordingly.
+<p> <pre> m_canvas = new <a href="qcanvas.html">QCanvas</a>( this );
+ m_canvas-&gt;<a href="qcanvas.html#resize">resize</a>( <a href="qwidget.html#width">width</a>(), height() );
+ m_canvasView = new CanvasView( m_canvas, &amp;m_elements, this );
+ <a href="qmainwindow.html#setCentralWidget">setCentralWidget</a>( m_canvasView );
+ m_canvasView-&gt;<a href="qwidget.html#show">show</a>();
+</pre>
+<p> We create a new <a href="qcanvas.html">QCanvas</a> and set its size to that of the chart form
+window's client area. We also create a <tt>CanvasView</tt> (our own subclass
+of <a href="qcanvasview.html">QCanvasView</a>) to display the QCanvas. We make the canvas view the
+chart form's main widget and show it.
+<p> <pre> if ( !filename.<a href="qstring.html#isEmpty">isEmpty</a>() )
+ load( filename );
+ else {
+ init();
+ m_elements[0].set( 20, red, 14, "Red" );
+ m_elements[1].set( 70, cyan, 2, "Cyan", darkGreen );
+ m_elements[2].set( 35, blue, 11, "Blue" );
+ m_elements[3].set( 55, yellow, 1, "Yellow", darkBlue );
+ m_elements[4].set( 80, magenta, 1, "Magenta" );
+ drawElements();
+ }
+</pre>
+<p> If we have a file to load we load it; otherwise we initialise our
+elements vector and draw a sample chart.
+<p> <pre> <a href="qmainwindow.html#statusBar">statusBar</a>()-&gt;message( "Ready", 2000 );
+</pre>
+<p> It is <em>vital</em> that we call statusBar() in the constructor, since the
+call ensures that a status bar is created for this main window.
+<p> <h3> init()
+</h3>
+<a name="2-1"></a><p> <pre> void ChartForm::init()
+ {
+ <a href="qwidget.html#setCaption">setCaption</a>( "Chart" );
+ m_filename = <a href="qstring.html#QString-null">QString::null</a>;
+ m_changed = FALSE;
+
+ m_elements[0] = Element( Element::INVALID, red );
+ m_elements[1] = Element( Element::INVALID, cyan );
+ m_elements[2] = Element( Element::INVALID, blue );
+</pre><tt>...</tt>
+<p> We use an init() function because we want to initialise the canvas and
+the elements (in the <tt>m_elements</tt> <tt>ElementVector</tt>) when the form is
+constructed, and also whenever the user loads an existing data set or
+creates a new data set.
+<p> We reset the caption and set the current filename to QString::null. We
+also populate the elements vector with invalid elements. This isn't
+necessary, but giving each element a different color is more
+convenient for the user since when they enter values each one will
+already have a unique color (which they can change of course).
+<p> <h2> The File Handling Actions
+</h2>
+<a name="3"></a><p> <h3> okToClear()
+</h3>
+<a name="3-1"></a><p> <pre> bool ChartForm::okToClear()
+ {
+ if ( m_changed ) {
+ <a href="qstring.html">QString</a> msg;
+ if ( m_filename.isEmpty() )
+ msg = "Unnamed chart ";
+ else
+ msg = QString( "Chart '%1'\n" ).arg( m_filename );
+ msg += "has been changed.";
+
+ int x = QMessageBox::<a href="qmessagebox.html#information">information</a>( this, "Chart -- Unsaved Changes",
+ msg, "&amp;Save", "Cancel", "&amp;Abandon",
+ 0, 1 );
+ switch( x ) {
+ case 0: // Save
+ fileSave();
+ break;
+ case 1: // Cancel
+ default:
+ return FALSE;
+ case 2: // Abandon
+ break;
+ }
+ }
+
+ return TRUE;
+ }
+</pre>
+<p> The okToClear() function is used to prompt the user to save their
+values if they have any unsaved data. It is used by several other
+functions.
+<p> <h3> fileNew()
+</h3>
+<a name="3-2"></a><p>
+
+<pre> void ChartForm::fileNew()
+ {
+ if ( okToClear() ) {
+ init();
+ drawElements();
+ }
+ }
+</pre>
+<p> When the user invokes the fileNew() action we call okToClear() to give
+them the opportunity to save any unsaved data. If they either save or
+abandon or have no unsaved data we re-initialise the elements vector
+and draw the default chart.
+<p> <center><table cellpadding="4" cellspacing="2" border="0">
+<tr bgcolor="#d0d0d0">
+<td valign="top">Should we also have invoked optionsSetData() to pop up the dialog
+through which the user can create and edit values, colors etc? You
+could try running the application as it is, and then try it having
+added a call to optionsSetData() and see which you prefer.
+</table></center>
+<p> <h3> fileOpen()
+</h3>
+<a name="3-3"></a><p> <pre> void ChartForm::fileOpen()
+ {
+ if ( !okToClear() )
+ return;
+
+ <a href="qstring.html">QString</a> filename = QFileDialog::<a href="qfiledialog.html#getOpenFileName">getOpenFileName</a>(
+ QString::null, "Charts (*.cht)", this,
+ "file open", "Chart -- File Open" );
+ <a name="x2567"></a> if ( !filename.<a href="qstring.html#isEmpty">isEmpty</a>() )
+ load( filename );
+ else
+ <a href="qmainwindow.html#statusBar">statusBar</a>()-&gt;message( "File Open abandoned", 2000 );
+ }
+</pre>
+<p> We check that it is okToClear(). If it is we use the static
+<a href="qfiledialog.html#getOpenFileName">QFileDialog::getOpenFileName</a>() function to get the name of the file
+the user wishes to load. If we get a filename we call load().
+<p> <h3> fileSaveAs()
+</h3>
+<a name="3-4"></a><p> <pre> void ChartForm::fileSaveAs()
+ {
+ <a href="qstring.html">QString</a> filename = QFileDialog::<a href="qfiledialog.html#getSaveFileName">getSaveFileName</a>(
+ QString::null, "Charts (*.cht)", this,
+ "file save as", "Chart -- File Save As" );
+ if ( !filename.<a href="qstring.html#isEmpty">isEmpty</a>() ) {
+ int answer = 0;
+ <a name="x2563"></a> if ( QFile::<a href="qfile.html#exists">exists</a>( filename ) )
+ <a name="x2566"></a> answer = QMessageBox::<a href="qmessagebox.html#warning">warning</a>(
+ this, "Chart -- Overwrite File",
+ QString( "Overwrite\n\'%1\'?" ).
+ arg( filename ),
+ "&amp;Yes", "&amp;No", QString::null, 1, 1 );
+ if ( answer == 0 ) {
+ m_filename = filename;
+ updateRecentFiles( filename );
+ fileSave();
+ return;
+ }
+ }
+ <a href="qmainwindow.html#statusBar">statusBar</a>()-&gt;message( "Saving abandoned", 2000 );
+ }
+</pre>
+<p> This function calls the static <a href="qfiledialog.html#getSaveFileName">QFileDialog::getSaveFileName</a>() to get
+the name of the file to save the data in. If the file exists we use a
+<a href="qmessagebox.html#warning">QMessageBox::warning</a>() to notify the user and give them the option of
+abandoning the save. If the file is to be saved we update the recently
+opened files list and call fileSave() (covered in <a href="tutorial2-07.html">File Handling</a>) to perform the save.
+<p> <h2> Managing a list of Recently Opened Files
+</h2>
+<a name="4"></a><p>
+
+<pre> <a href="qstringlist.html">QStringList</a> m_recentFiles;
+</pre>
+<p> We hold the list of recently opened files in a string list.
+<p>
+
+<pre> void ChartForm::updateRecentFilesMenu()
+ {
+ for ( int i = 0; i &lt; MAX_RECENTFILES; ++i ) {
+ if ( fileMenu-&gt;<a href="qmenudata.html#findItem">findItem</a>( i ) )
+ fileMenu-&gt;<a href="qmenudata.html#removeItem">removeItem</a>( i );
+ if ( i &lt; int(m_recentFiles.count()) )
+ fileMenu-&gt;<a href="qmenudata.html#insertItem">insertItem</a>( QString( "&amp;%1 %2" ).
+ arg( i + 1 ).arg( m_recentFiles[i] ),
+ this, SLOT( fileOpenRecent(int) ),
+ 0, i );
+ }
+ }
+</pre>
+<p> This function is called (usually via updateRecentFiles()) whenever the
+user opens an existing file or saves a new file. For each file in the
+string list we insert a new menu item. We prefix each filename with an
+underlined number from <u>1</u> to <u>9</u> to support keyboard access
+(e.g. <tt>Alt+F, 2</tt> to open the second file in the list). We give the
+menu item an id which is the same as the index position of the item in
+the string list, and connect each menu item to the fileOpenRecent()
+slot. The old file menu items are deleted at the same time by going
+through each possible recent file menu item id. This works because the
+other file menu items had ids created by Qt (all of which are &lt; 0);
+whereas the menu items we're creating all have ids &gt;= 0.
+<p>
+
+<pre> void ChartForm::updateRecentFiles( const <a href="qstring.html">QString</a>&amp; filename )
+ {
+ if ( m_recentFiles.find( filename ) != m_recentFiles.end() )
+ return;
+
+ m_recentFiles.push_back( filename );
+ if ( m_recentFiles.count() &gt; MAX_RECENTFILES )
+ m_recentFiles.pop_front();
+
+ updateRecentFilesMenu();
+ }
+</pre>
+<p> This is called when the user opens an existing file or saves a new
+file. If the file is already in the list it simply returns. Otherwise
+the file is added to the end of the list and if the list is too large
+(&gt; 9 files) the first (oldest) is removed. updateRecentFilesMenu() is
+then called to recreate the list of recently used files in the File
+menu.
+<p>
+
+<pre> void ChartForm::fileOpenRecent( int index )
+ {
+ if ( !okToClear() )
+ return;
+
+ load( m_recentFiles[index] );
+ }
+</pre>
+<p> When the user selects a recently opened file the fileOpenRecent() slot
+is called with the menu id of the file they have selected. Because we
+made the file menu ids equal to the files' index positions in the
+<tt>m_recentFiles</tt> list we can simply load the file indexed by the menu
+item id.
+<p> <h2> Quiting
+</h2>
+<a name="5"></a><p> <pre> void ChartForm::fileQuit()
+ {
+ if ( okToClear() ) {
+ saveOptions();
+ qApp-&gt;<a href="qapplication.html#exit">exit</a>( 0 );
+ }
+ }
+</pre>
+<p> When the user quits we give them the opportunity to save any unsaved
+data (okToClear()) then save their options, e.g. window size and
+position, chart type, etc., before terminating.
+<p> <pre> void ChartForm::saveOptions()
+ {
+ <a href="qsettings.html">QSettings</a> settings;
+ settings.<a href="qsettings.html#insertSearchPath">insertSearchPath</a>( QSettings::Windows, WINDOWS_REGISTRY );
+ settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "WindowWidth", width() );
+ settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "WindowHeight", height() );
+ settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "WindowX", x() );
+ settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "WindowY", y() );
+ settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "ChartType", int(m_chartType) );
+ settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "AddValues", int(m_addValues) );
+ settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "Decimals", m_decimalPlaces );
+ settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "Font", m_font.toString() );
+ for ( int i = 0; i &lt; int(m_recentFiles.count()); ++i )
+ settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "File" + QString::number( i + 1 ),
+ m_recentFiles[i] );
+ }
+</pre>
+<p> Saving the user's options using <a href="qsettings.html">QSettings</a> is straight-forward.
+<p> <h2> Custom Dialogs
+</h2>
+<a name="6"></a><p> We want the user to be able to set some options manually and to create
+and edit values, value colors, etc.
+<p>
+
+<pre> void ChartForm::optionsSetOptions()
+ {
+ OptionsForm *optionsForm = new OptionsForm( this );
+ optionsForm-&gt;chartTypeComboBox-&gt;setCurrentItem( m_chartType );
+ optionsForm-&gt;<a href="qwidget.html#setFont">setFont</a>( m_font );
+</pre><pre> if ( optionsForm-&gt;<a href="qdialog.html#exec">exec</a>() ) {
+ setChartType( ChartType(
+ optionsForm-&gt;chartTypeComboBox-&gt;currentItem()) );
+ m_font = optionsForm-&gt;<a href="qwidget.html#font">font</a>();
+</pre><pre> drawElements();
+ }
+ delete optionsForm;
+ }
+</pre>
+<p> The form for setting options is provided by our custom <tt>OptionsForm</tt>
+covered in <a href="tutorial2-09.html">Setting Options</a>. The
+options form is a standard "dumb" dialog: we create an instance, set
+all its GUI elements to the relevant settings, and if the user clicked
+"OK" (exec() returns a true value) we read back settings from the GUI
+elements.
+<p>
+
+<pre> void ChartForm::optionsSetData()
+ {
+ SetDataForm *setDataForm = new SetDataForm( &amp;m_elements, m_decimalPlaces, this );
+ if ( setDataForm-&gt;<a href="qdialog.html#exec">exec</a>() ) {
+ m_changed = TRUE;
+ drawElements();
+ }
+ delete setDataForm;
+ }
+</pre>
+<p> The form for creating and editing chart data is provided by our custom
+<tt>SetDataForm</tt> covered in <a href="tutorial2-08.html">Taking Data</a>.
+This form is a "smart" dialog. We pass in the data structure we want
+to work on, and the dialog handles the presentation of the data
+structure itself. If the user clicks "OK" the dialog will update the
+data structure and exec() will return a true value. All we need to do
+in optionsSetData() if the user changed the data is mark the chart as
+changed and call drawElements() to redraw the chart with the new and
+updated data.
+<p> <p align="right">
+<a href="tutorial2-04.html">&laquo; Mainly Easy</a> |
+<a href="tutorial2.html">Contents</a> |
+<a href="tutorial2-06.html">Canvas Control &raquo;</a>
+</p>
+<p>
+<!-- eof -->
+<p><address><hr><div align=center>
+<table width=100% cellspacing=0 border=0><tr>
+<td>Copyright &copy; 2007
+<a href="troll.html">Trolltech</a><td align=center><a href="trademarks.html">Trademarks</a>
+<td align=right><div align=right>Qt 3.3.8</div>
+</table></div></address></body>
+</html>