• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

The MinGW.org Installation Manager Tool


Commit MetaInfo

Revision95551e342c568545e2a60ec2e82304ba1291799c (tree)
Time2012-10-11 19:02:17
AuthorKeith Marshall <keithmarshall@user...>
CommiterKeith Marshall

Log Message

Implement preliminary GUI mode data sheet compiler.

Change Summary

Incremental Difference

--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,37 @@
1+2012-10-11 Keith Marshall <keithmarshall@users.sourceforge.net>
2+
3+ Implement preliminary GUI mode data sheet compiler.
4+
5+ * src/guimain.h (DataSheetMaker): New class; forward declare it.
6+ (AppWindowMaker::InitPackageTabControl): New private method; declare it.
7+ (AppWindowMaker::DataSheet, AppWindowMaker::PackageTabControl):
8+ (AppWindowMaker::TabDataPane, AppWindowMaker::PackageTabPane): New
9+ private data members; declare them.
10+ (ID_SASH_WINDOW_PANE_CLASS): New string resource identifier; define it.
11+ (ID_PACKAGE_DATASHEET, ID_PACKAGE_TABPANE): New window identifiers;
12+ define them.
13+
14+ * src/guidata.rc (ID_SASH_WINDOW_PANE_CLASS): Specify string resource.
15+
16+ * src/pkgview.cpp (AppWindowMaker::OnSize): Add handler for...
17+ (ID_PACKAGE_TABPANE, ID_PACKAGE_TABCONTROL, ID_PACKAGE_DATASHHET):
18+ ...this group of sash pane child window entities.
19+ (AppWindowMaker::OnNotify): Comment out disused stub implementation.
20+
21+ * src/pkgshow.cpp (pkgUTF8Parser): Factor out class declaration...
22+ (UTF32_MAX, UTF32_OVERFLOW, UTF32_INVALID): ...these definitions...
23+ (utf32_t): ...and this typedef; relocate to, and #include...
24+ * src/pkgdata.h: ...this new header file.
25+
26+ * src/pkgdata.cpp (DataSheetMaker): New class; declare and implement it.
27+ (AppWindowMaker::InitPackageTabControl): Implement it.
28+ (AppWindowMaker::OnNotify): Reimplement it.
29+
30+ * Makefile.in (GUIMAIN_OBJECTS): Add pkgdata.OBJEXT
31+
32+ * src/guixmld.cpp (AppWindowMaker::Invoked): Add call to invoke
33+ AppWindowMaker::InitPackageTabControl()
34+
135 2012-10-09 Keith Marshall <keithmarshall@users.sourceforge.net>
236
337 Remove a grossly heuristic list view hack.
--- a/Makefile.in
+++ b/Makefile.in
@@ -91,7 +91,7 @@ CLI_EXE_OBJECTS = \
9191
9292 GUIMAIN_OBJECTS = \
9393 guimain.$(OBJEXT) guidata.$(OBJEXT) guixmld.$(OBJEXT) guidmh.$(OBJEXT) \
94- approot.$(OBJEXT) pkgview.$(OBJEXT) pkglist.$(OBJEXT)
94+ approot.$(OBJEXT) pkgview.$(OBJEXT) pkglist.$(OBJEXT) pkgdata.$(OBJEXT)
9595
9696 GUIMAIN_LIBS = -lwtklite -lcomctl32
9797
--- a/src/guidata.rc
+++ b/src/guidata.rc
@@ -59,6 +59,7 @@ STRINGTABLE DISCARDABLE
5959 BEGIN
6060 ID_MAIN_WINDOW_CLASS "mingw-get-gui"
6161 ID_MAIN_WINDOW_CAPTION "MinGW Installation Manager"
62+ ID_SASH_WINDOW_PANE_CLASS "mingw-get-sash-pane"
6263 ID_FONT_PREF "Verdana"
6364 END
6465
--- a/src/guimain.h
+++ b/src/guimain.h
@@ -35,6 +35,7 @@
3535 #define ID_MAIN_WINDOW_CAPTION 102
3636 #define ID_FONT_PREF 103
3737
38+#define ID_SASH_WINDOW_PANE_CLASS 104
3839 #define ID_HORIZONTAL_SASH 105
3940 #define ID_VERTICAL_SASH 106
4041
@@ -43,6 +44,8 @@
4344 #define ID_PACKAGE_TREEVIEW 201
4445 #define ID_PACKAGE_LISTVIEW 202
4546 #define ID_PACKAGE_TABCONTROL 203
47+#define ID_PACKAGE_DATASHEET 204
48+#define ID_PACKAGE_TABPANE 205
4649
4750 #define IDM_MAIN_MENU 300
4851 #define IDM_REPO_UPDATE 301
@@ -96,6 +99,7 @@
9699 #include <commctrl.h>
97100
98101 class pkgXmlDocument;
102+class DataSheetMaker;
99103
100104 class AppWindowMaker;
101105 inline AppWindowMaker *GetAppWindow( HWND lookup )
@@ -137,6 +141,11 @@ class AppWindowMaker: public WTK::MainWindowMaker
137141 void InitPackageListView( void );
138142 void ClearPackageList( void ){ ListView_DeleteAllItems( PackageListView ); }
139143 void UpdatePackageList( void );
144+
145+ DataSheetMaker *DataSheet;
146+ WTK::ChildWindowMaker *TabDataPane;
147+ HWND PackageTabControl, PackageTabPane;
148+ void InitPackageTabControl();
140149 };
141150
142151 #endif /* ! RC_INVOKED */
--- a/src/guixmld.cpp
+++ b/src/guixmld.cpp
@@ -50,6 +50,11 @@ int AppWindowMaker::Invoked( void )
5050 LoadPackageData();
5151 InitPackageListView();
5252
53+ /* Initialise the data-sheet tab control, displaying the default
54+ * "no package selected" message.
55+ */
56+ InitPackageTabControl();
57+
5358 /* Force a layout adjustment, to ensure that the displayed
5459 * data controls are correctly populated.
5560 */
--- /dev/null
+++ b/src/pkgdata.cpp
@@ -0,0 +1,747 @@
1+/*
2+ * pkgdata.cpp
3+ *
4+ * $Id$
5+ *
6+ * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7+ * Copyright (C) 2012, MinGW Project
8+ *
9+ *
10+ * Implementation of the classes and methods required to support the
11+ * GUI tabbed view of package information "data sheets".
12+ *
13+ *
14+ * This is free software. Permission is granted to copy, modify and
15+ * redistribute this software, under the provisions of the GNU General
16+ * Public License, Version 3, (or, at your option, any later version),
17+ * as published by the Free Software Foundation; see the file COPYING
18+ * for licensing details.
19+ *
20+ * Note, in particular, that this software is provided "as is", in the
21+ * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
22+ * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
23+ * PARTICULAR PURPOSE. Under no circumstances will the author, or the
24+ * MinGW Project, accept liability for any damages, however caused,
25+ * arising from the use of this software.
26+ *
27+ */
28+#include <stdlib.h>
29+#include <string.h>
30+
31+#include "dmh.h"
32+
33+#include "guimain.h"
34+#include "pkgbase.h"
35+#include "pkgdata.h"
36+#include "pkgkeys.h"
37+#include "pkglist.h"
38+
39+using WTK::StringResource;
40+using WTK::WindowClassMaker;
41+using WTK::ChildWindowMaker;
42+
43+/* Margin settings, controlling the positioning of the
44+ * active viewport within the data sheet display pane.
45+ */
46+#define TOP_MARGIN 5
47+#define PARAGRAPH_MARGIN 5
48+#define LEFT_MARGIN 8
49+#define RIGHT_MARGIN 8
50+#define BOTTOM_MARGIN 5
51+
52+class pkgTroffLayoutEngine: public pkgUTF8Parser
53+{
54+ /* A privately implemented class, supporting a simplified troff
55+ * style layout for a UTF-8 text stream, within a scrolling GUI
56+ * display pane.
57+ */
58+ public:
59+ pkgTroffLayoutEngine( const char *input, long displacement ):
60+ pkgUTF8Parser( input ), curr( this ), offset( displacement ){}
61+ bool WriteLn( HDC, RECT * );
62+
63+ private:
64+ inline bool IsReady();
65+ pkgTroffLayoutEngine *curr;
66+ long offset;
67+};
68+
69+inline bool pkgTroffLayoutEngine::IsReady()
70+{
71+ /* Private helper method, used to position the input stream to
72+ * the next parseable token, if any, for processing by WriteLn.
73+ */
74+ while( (curr != NULL) && ((curr->length == 0) || (curr->text == NULL)) )
75+ curr = (pkgTroffLayoutEngine *)(curr->next);
76+ return (curr != NULL);
77+}
78+
79+bool pkgTroffLayoutEngine::WriteLn( HDC canvas, RECT *bounds )
80+{
81+ /* Method to extract a single line of text from the UTF-8 stream,
82+ * (if any is available for processing), format it as appropriate
83+ * for display, and write it into the display pane.
84+ */
85+ if( IsReady() )
86+ {
87+ /* Initialise a buffer, in which to compile the formatted text
88+ * record for display; establish and initialise the counters for
89+ * controlling the formatting process.
90+ */
91+ wchar_t linebuf[1 + strlen( curr->text )];
92+ long curr_width, new_width = 0, max_width = bounds->right - bounds->left;
93+ int filled, extent = 0, fold = 0;
94+
95+ /* Establish default tracking and justification settings.
96+ */
97+ SetTextCharacterExtra( canvas, 0 );
98+ SetTextJustification( canvas, 0, 0 );
99+
100+ /* Copy text from the input stream, to fill the transfer buffer
101+ * up to the maximum permitted output line length, or until the
102+ * input stream has been exhausted.
103+ */
104+ SIZE span;
105+ do { if( curr->length > 0 )
106+ { /* There is at least one more word of input text to copy,
107+ * and there may be sufficient output space to accommodate
108+ * it; record the space filled so far, up to the end of the
109+ * preceding word, (if any)...
110+ */
111+ filled = extent;
112+ curr_width = new_width;
113+ if( extent > 0 )
114+ {
115+ /* ...and, when there was a preceding word, add white
116+ * space and record a potential line folding point.
117+ */
118+ linebuf[extent++] = L'\x20';
119+ ++fold;
120+ }
121+
122+ /* Append one word, copied from the input stream to the
123+ * output line buffer.
124+ */
125+ const char *mark = curr->text;
126+ for( int i = 0; i < curr->length; ++i )
127+ linebuf[extent++] = GetCodePoint( mark = ScanBuffer( mark ) );
128+
129+ /* Check the effective output line length which would be
130+ * required to accommodate the extended output record...
131+ */
132+ if( GetTextExtentPoint32W( canvas, linebuf, extent, &span ) )
133+ {
134+ /* ...and while it still fits within the maximum width
135+ * of the display pane...
136+ */
137+ if( max_width >= (new_width = span.cx) )
138+ /*
139+ * ...accept the current input word, and move on to
140+ * see if we can accommodate another.
141+ */
142+ curr = (pkgTroffLayoutEngine *)(curr->next);
143+ }
144+ else
145+ /* In the event of any error in evaluating the output
146+ * line length, reject any remaining input.
147+ */
148+ curr = NULL;
149+ }
150+ else
151+ /* We found a zero-length entity in the input stream;
152+ * ignore it, and move on to the next, if any.
153+ */
154+ curr = (pkgTroffLayoutEngine *)(curr->next);
155+
156+ /* Continue the cycle, unless we have exhausted the input
157+ * stream, or we have run out of available output space.
158+ */
159+ } while( (curr != NULL) && (max_width > new_width) );
160+
161+ /* When we've collected a complete line of output text...
162+ */
163+ if( (bounds->top >= (TOP_MARGIN + offset))
164+ && ((bounds->bottom + offset) >= (bounds->top + span.cy)) )
165+ {
166+ /* ...and when it is to be positioned vertically within the
167+ * bounds of the active viewport...
168+ */
169+ if( bounds->top < (TOP_MARGIN + offset + span.cy) )
170+ /*
171+ * ...when it is the topmost visible line, ensure that it
172+ * is vertically aligned flush with the top margin.
173+ */
174+ bounds->top = TOP_MARGIN + offset;
175+
176+ /* Check if the output line collection loop, above, ended
177+ * on an attempt to over-fill the buffer...
178+ */
179+ if( max_width >= new_width )
180+ /*
181+ * ...but when it did not, handle it as a partially filled
182+ * line, which is thus exempt from right justification.
183+ */
184+ filled = extent;
185+
186+ /* When the output line is over-filled, then we will attempt
187+ * to fold it at the last counted fold point, and then insert
188+ * padding space at each remaining internal fold point, so as
189+ * to achieve flush left/right justification; (note that we
190+ * decrement the fold count here, because the point at which
191+ * we fold the line has been included in the count, but we
192+ * don't want to add padding space at the right margin).
193+ */
194+ else if( --fold > 0 )
195+ {
196+ /* To adjust the output line, we first compute the number
197+ * of padding PIXELS required, then...
198+ */
199+ long padding;
200+ if( (padding = max_width - curr_width) >= filled )
201+ {
202+ /* ...in the event that this is no fewer than the number
203+ * of physical GLYPHS to be output, we adjust the tracking
204+ * to accommodate as many padding pixels as possible, with
205+ * ONE additional inter-glyph tracking pixel per glyph...
206+ */
207+ SetTextCharacterExtra( canvas, 1 );
208+ if( GetTextExtentPoint32W( canvas, linebuf, filled, &span ) )
209+ /*
210+ * ...and then, we recompute the number of additional
211+ * inter-word padding pixels, if any, which are still
212+ * required.
213+ */
214+ padding = max_width - span.cx;
215+
216+ /* In the event that adjustment of tracking fails, we
217+ * must reset it, because the padding count remains as
218+ * computed for default tracking.
219+ */
220+ else
221+ SetTextCharacterExtra( canvas, 0 );
222+ }
223+ /* Now, distribute the padding pixels among the remaining
224+ * inter-word (fold) spaces within the output line...
225+ */
226+ SetTextJustification( canvas, padding, fold );
227+ }
228+ /* ...and write the output line at the designated position
229+ * within the display viewport.
230+ */
231+ TextOutW( canvas, bounds->left, bounds->top - offset, linebuf, filled );
232+ }
233+ /* Finally, adjust the top boundary of the viewport, to indicate
234+ * where the NEXT output line, if any, is to be positioned, and
235+ * return TRUE, to indicate that an output line was processed.
236+ */
237+ bounds->top += span.cy;
238+ return true;
239+ }
240+ /* If we get to here, then there was nothing in the input stream to
241+ * be processed; return FALSE, to indicate this.
242+ */
243+ return false;
244+}
245+
246+class DataSheetMaker: public ChildWindowMaker
247+{
248+ /* Specialised variant of the standard child window class, augmented
249+ * to provide the custom methods for formatting and displaying package
250+ * data sheet content within the tabbed data display pane.
251+ *
252+ * FIXME: we may eventually need to make this class externally visible,
253+ * but for now we implement it as a locally declared class.
254+ */
255+ public:
256+ DataSheetMaker( HINSTANCE inst ): ChildWindowMaker( inst ),
257+ PackageRef( NULL ), DataClass( NULL ){}
258+ virtual void DisplayData( HWND, HWND );
259+
260+ private:
261+ virtual long OnCreate();
262+ virtual long OnVerticalScroll( int, int, HWND );
263+ virtual long OnPaint();
264+
265+ HWND PackageRef, DataClass;
266+ HDC canvas; RECT bounding_box;
267+ HFONT NormalFont, BoldFont;
268+
269+ long offset; char *desc;
270+ inline void DisplayDescription( pkgXmlNode * );
271+ void ComposeDescription( pkgXmlNode *, pkgXmlNode * );
272+ inline void FormatText( HDC, const char * );
273+ long LineSpacing;
274+};
275+
276+enum
277+{ /* Tab identifiers for the available data sheet collection.
278+ */
279+ PKG_DATASHEET_GENERAL = 0,
280+ PKG_DATASHEET_DESCRIPTION,
281+ PKG_DATASHEET_DEPENDENCIES,
282+ PKG_DATASHEET_INSTALLED_FILES,
283+ PKG_DATASHEET_VERSIONS
284+};
285+
286+long DataSheetMaker::OnCreate()
287+{
288+ /* Method called when creating a data sheet window; initialise font
289+ * preferences and line spacing for any instance of a DataSheetMaker
290+ * object.
291+ *
292+ * Initially, we match the font properties to the default GUI font...
293+ */
294+ LOGFONT font_info;
295+ HFONT font = (HFONT)(GetStockObject( DEFAULT_GUI_FONT ));
296+ GetObject( BoldFont = NormalFont = font, sizeof( LOGFONT ), &font_info );
297+
298+ /* ...then, we substitute the preferred type face.
299+ */
300+ strcpy( (char *)(&(font_info.lfFaceName)), "Verdana" );
301+ if( (font = CreateFontIndirect( &font_info )) != NULL )
302+ {
303+ /* On successfully creating the preferred font, we may discard
304+ * the original default font object, and assign our preference
305+ * as both the normal and bold working font...
306+ */
307+ DeleteObject( NormalFont );
308+ BoldFont = NormalFont = font;
309+
310+ /* ...before adjusting the weight for the bold variant...
311+ */
312+ font_info.lfWeight = FW_BOLD;
313+ if( (font = CreateFontIndirect( &font_info )) != NULL )
314+ {
315+ /* ...and reassigning when successful.
316+ */
317+ BoldFont = font;
318+ }
319+ }
320+
321+ /* Finally, we determine the line spacing (in pixels) for a line
322+ * of text, in the preferred normal font, within the device context
323+ * for the data sheet window.
324+ */
325+ SIZE span;
326+ HDC canvas = GetDC( AppWindow );
327+ SelectObject( canvas, NormalFont );
328+ LineSpacing = GetTextExtentPoint32A( canvas, "Height", 6, &span ) ? span.cy : 13;
329+ ReleaseDC( AppWindow, canvas );
330+
331+ return offset = 0;
332+}
333+
334+void DataSheetMaker::DisplayData( HWND tab, HWND package )
335+{
336+ /* Method to force a refresh of the data sheet display pane.
337+ */
338+ PackageRef = package; DataClass = tab;
339+ InvalidateRect( AppWindow, NULL, TRUE );
340+ UpdateWindow( AppWindow );
341+}
342+
343+inline void DataSheetMaker::FormatText( HDC canvas, const char *text )
344+{
345+ /* Helper method to transfer text to the display device, formatting
346+ * it to fill as many lines of the viewing window as may be required,
347+ * justifying for flush margins at both left and right.
348+ */
349+ pkgTroffLayoutEngine page( text, offset );
350+ while( page.WriteLn( canvas, &bounding_box ) )
351+ ;
352+}
353+
354+inline void DataSheetMaker::DisplayDescription( pkgXmlNode *ref )
355+{
356+ /* A convenience method to invoke the recursive retrieval of any
357+ * package description, without requiring a look-up of the address
358+ * of the XML document root at every level of recursion.
359+ */
360+ ComposeDescription( ref, ref->GetDocumentRoot() );
361+}
362+
363+void DataSheetMaker::ComposeDescription( pkgXmlNode *ref, pkgXmlNode *root )
364+{
365+ /* Recursive method to compile a package description, from text
366+ * fragments retrieved from the XML specification document, and
367+ * present it in a data sheet window.
368+ */
369+ if( ref != root )
370+ {
371+ /* Recursively walk the XML hierarchy, until we reach the
372+ * document root...
373+ */
374+ ComposeDescription( ref->GetParent(), root );
375+
376+ /* ...then unwind the recursion, selecting "description"
377+ * elements, (if any), at each level...
378+ */
379+ if( (root = ref->FindFirstAssociate( description_key )) != NULL )
380+ {
381+ /* ...formatting each, paragraph by paragraph, for display
382+ * within the viewport bounding box of the data sheet...
383+ */
384+ do { if( (ref = root->FindFirstAssociate( paragraph_key )) != NULL )
385+ do { if( bounding_box.top > (TOP_MARGIN + offset) )
386+ /*
387+ * When this is not the top-most visible
388+ * paragraph, within the viewport, displace
389+ * it downwards by one paragraph margin from
390+ * its predecessor...
391+ */
392+ bounding_box.top += PARAGRAPH_MARGIN;
393+
394+ /* ...before laying out the visible text of
395+ * this paragraph, (if any).
396+ */
397+ FormatText( canvas, ref->GetText() );
398+
399+ /* Cycle, to process any further paragraphs
400+ * which are included within the current
401+ * description block...
402+ */
403+ } while( (ref = ref->FindNextAssociate( paragraph_key )) != NULL );
404+
405+ /* ...and ultimately, for any additional description blocks
406+ * which may have been specified within the current XML element,
407+ * at the current hierarchical nesting level.
408+ */
409+ } while( (root = root->FindNextAssociate( description_key )) != NULL );
410+ }
411+ }
412+}
413+
414+long DataSheetMaker::OnPaint()
415+{
416+ /* Handler for WM_PAINT message messages, sent to any window
417+ * ascribed to the DataSheetMaker class.
418+ */
419+ PAINTSTRUCT content;
420+ canvas = BeginPaint( AppWindow, &content );
421+ HFONT original_font = (HFONT)(SelectObject( canvas, NormalFont ));
422+
423+ /* Establish a viewport, with a suitable margin, within the
424+ * bounding rectangle of the window.
425+ */
426+ GetClientRect( AppWindow, &bounding_box );
427+ bounding_box.left += LEFT_MARGIN; bounding_box.right -= RIGHT_MARGIN;
428+ bounding_box.top += TOP_MARGIN; bounding_box.bottom -= BOTTOM_MARGIN;
429+
430+ /* Provide bindings for a vertical scrollbar...
431+ */
432+ SCROLLINFO scrollbar;
433+ if( offset == 0 )
434+ {
435+ /* ...and prepare to initialise it, when we redraw
436+ * the data sheet from the top.
437+ */
438+ scrollbar.cbSize = sizeof( scrollbar );
439+ scrollbar.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
440+ scrollbar.nPos = scrollbar.nMin = bounding_box.top;
441+ scrollbar.nPage = 1 + bounding_box.bottom - bounding_box.top;
442+ scrollbar.nMax = bounding_box.bottom;
443+ }
444+
445+ /* Identify the package, as selected in the package list window,
446+ * for which the data sheet is to be compiled.
447+ */
448+ LVITEM lookup;
449+ lookup.iItem = (PackageRef != NULL)
450+ ? ListView_GetNextItem( PackageRef, (WPARAM)(-1), LVIS_SELECTED )
451+ : -1;
452+
453+ if( lookup.iItem >= 0 )
454+ {
455+ /* There is an active package selection; identify the selected
456+ * data sheet tab, if any...
457+ */
458+ int tab = ( DataClass != NULL ) ? TabCtrl_GetCurSel( DataClass )
459+ /*
460+ * ...otherwise default to the package description.
461+ */
462+ : PKG_DATASHEET_DESCRIPTION;
463+
464+ /* Retrieve the package title from the list view; assign it as
465+ * a bold face heading in the data sheet view...
466+ */
467+ char desc[256];
468+ SelectObject( canvas, BoldFont );
469+ ListView_GetItemText( PackageRef, lookup.iItem, 5, desc, sizeof( desc ) );
470+ FormatText( canvas, desc );
471+ if( offset > 0 )
472+ {
473+ /* ...adjusting as appropriate, when the heading is scrolled
474+ * out of the viewport.
475+ */
476+ if( (offset -= (bounding_box.top - TOP_MARGIN)) < 0 )
477+ offset = 0;
478+ bounding_box.top = TOP_MARGIN;
479+ }
480+
481+ /* Revert to normal typeface, in preparation for compilation
482+ * of the selected data sheet.
483+ */
484+ SelectObject( canvas, NormalFont );
485+ switch( tab )
486+ {
487+ case PKG_DATASHEET_DESCRIPTION:
488+ /* This represents the package description, provided by
489+ * the package maintainer, within the XML specification.
490+ */
491+ lookup.iSubItem = 0;
492+ lookup.mask = LVIF_PARAM;
493+ ListView_GetItem( PackageRef, &lookup );
494+ DisplayDescription( (pkgXmlNode *)(lookup.lParam) );
495+ break;
496+
497+ default:
498+ /* Handle requests for data sheets for which we have yet
499+ * to provide a compiling routine.
500+ */
501+ bounding_box.top += TOP_MARGIN;
502+ FormatText( canvas,
503+ "FIXME:data sheet unavailable; a compiler for this "
504+ "data category has yet to be implemented."
505+ );
506+ }
507+ }
508+ else
509+ { /* There is no active package selection; advise accordingly.
510+ */
511+ bounding_box.top += TOP_MARGIN << 1;
512+ FormatText( canvas,
513+ "No package selected."
514+ );
515+ bounding_box.top += PARAGRAPH_MARGIN << 1;
516+ FormatText( canvas,
517+ "Please select a package from the list above, "
518+ "to view related data."
519+ );
520+ }
521+
522+ /* When redrawing the data sheet window from the top...
523+ */
524+ if( offset == 0 )
525+ {
526+ /* ...adjust the scrolling range to accommodate the full extent
527+ * of the data sheet text, and initialise the scrollbar control.
528+ */
529+ if( bounding_box.top > bounding_box.bottom )
530+ scrollbar.nMax = bounding_box.top;
531+ SetScrollInfo( AppWindow, SB_VERT, &scrollbar, TRUE );
532+ }
533+
534+ /* Finally, restore the original (default) font assignment
535+ * for the data sheet window, complete the redraw action, and
536+ * we are done.
537+ */
538+ SelectObject( canvas, original_font );
539+ EndPaint( AppWindow, &content );
540+ return EXIT_SUCCESS;
541+}
542+
543+long DataSheetMaker::OnVerticalScroll( int req, int pos, HWND ctrl )
544+{
545+ /* Handler for events signalled by the vertical scrollbar control,
546+ * (if any), in any window ascribed to the DataSheetMaker class.
547+ */
548+ SCROLLINFO scrollbar;
549+ scrollbar.fMask = SIF_ALL;
550+ scrollbar.cbSize = sizeof( scrollbar );
551+ GetScrollInfo( AppWindow, SB_VERT, &scrollbar );
552+
553+ /* Save the original "thumb" position.
554+ */
555+ long origin = scrollbar.nPos;
556+ switch( req )
557+ {
558+ /* Identify, and process the event message.
559+ */
560+ case SB_LINEUP:
561+ /* User clicked the "scroll-up" button; move the
562+ * "thumb" up by a distance equivalent to the height
563+ * of a single line of text.
564+ */
565+ scrollbar.nPos -= LineSpacing;
566+ break;
567+
568+ case SB_LINEDOWN:
569+ /* Similarly, for a click on the "scroll-down" button,
570+ * move the "thumb" down by one line height.
571+ */
572+ scrollbar.nPos += LineSpacing;
573+ break;
574+
575+ case SB_PAGEUP:
576+ /* User clicked the scrollbar region above the "thumb";
577+ * move the "thumb" up by half of the viewport height.
578+ */
579+ scrollbar.nPos -= scrollbar.nPage >> 1;
580+ break;
581+
582+ case SB_PAGEDOWN:
583+ /* Similarly, for a click below the "thumb", move it
584+ * down by half of the viewport height.
585+ */
586+ scrollbar.nPos += scrollbar.nPage >> 1;
587+ break;
588+
589+ case SB_THUMBTRACK:
590+ /* User is dragging...
591+ */
592+ case SB_THUMBPOSITION:
593+ /* ...or has just finished dragging the "thumb"; move it
594+ * by the distance it has been dragged.
595+ */
596+ scrollbar.nPos = scrollbar.nTrackPos;
597+
598+ case SB_ENDSCROLL:
599+ /* Preceding scrollbar event has completed; we do not need
600+ * to take any specific action here.
601+ */
602+ break;
603+
604+ default:
605+ /* We received an unexpected scrollbar event message...
606+ */
607+ dmh_notify( DMH_WARNING,
608+ "Unhandled scrollbar message: request = %d\n", req
609+ );
610+ }
611+ /* Update the scrollbar control, to capture any change in
612+ * "thumb" position...
613+ */
614+ scrollbar.fMask = SIF_POS;
615+ SetScrollInfo( AppWindow, SB_VERT, &scrollbar, TRUE );
616+
617+ /* ...then read it back, since the control hay have adjusted
618+ * the actual recorded position.
619+ */
620+ GetScrollInfo( AppWindow, SB_VERT, &scrollbar );
621+ if( scrollbar.nPos != origin )
622+ {
623+ /* When the "thumb" has moved, force a redraw of the data
624+ * sheet window, to capture any change in the visible text.
625+ */
626+ offset = scrollbar.nPos - scrollbar.nMin;
627+ InvalidateRect( AppWindow, NULL, TRUE );
628+ UpdateWindow( AppWindow );
629+
630+ /* Reset the default starting point, so that any subsequent
631+ * redraw will favour a "redraw-from-top"...
632+ */
633+ offset = 0;
634+ }
635+ /* ...and we are done.
636+ */
637+ return EXIT_SUCCESS;
638+}
639+
640+void AppWindowMaker::InitPackageTabControl()
641+{
642+ /* Create and initialise a TabControl window, in which to present
643+ * miscellaneous package information...
644+ */
645+ WindowClassMaker AppWindowRegistry( AppInstance );
646+ StringResource ClassName( AppInstance, ID_SASH_WINDOW_PANE_CLASS );
647+ AppWindowRegistry.Register( ClassName );
648+
649+ /* Package data sheets will be displayed in a derived child window
650+ * which we create as a member of the SASH_WINDOW_PANE_CLASS; it will
651+ * ultimately be displayed below the tab bar, within the tab control
652+ * region, with content dynamically painted on the basis of package
653+ * selection, (in the package list pane), and tab selection.
654+ */
655+ DataSheet = new DataSheetMaker( AppInstance );
656+ PackageTabPane = DataSheet->Create( ID_PACKAGE_DATASHEET,
657+ AppWindow, ClassName, WS_VSCROLL | WS_BORDER
658+ );
659+
660+ /* The tab control itself is the standard control, selected from
661+ * the common controls library.
662+ */
663+ PackageTabControl = CreateWindow( WC_TABCONTROL, NULL,
664+ WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS, 0, 0, 0, 0,
665+ AppWindow, (HMENU)(ID_PACKAGE_TABCONTROL),
666+ AppInstance, NULL
667+ );
668+
669+ /* Keep the font for tab labels consistent with our preference,
670+ * as assigned to the main application window.
671+ */
672+ SendMessage( PackageTabControl, WM_SETFONT, (WPARAM)(DefaultFont), TRUE );
673+
674+ /* Create the designated set of tabs, with appropriate labels...
675+ */
676+ TCITEM tab;
677+ tab.mask = TCIF_TEXT;
678+ char *TabLegend[] =
679+ { "General", "Description", "Dependencies", "Installed Files", "Versions",
680+
681+ /* ...with a NULL sentinel marking the preceding label as
682+ * the last in the list.
683+ */
684+ NULL
685+ };
686+ for( int i = 0; TabLegend[i] != NULL; ++i )
687+ {
688+ /* This loop assumes responsibility for actual tab creation...
689+ */
690+ tab.pszText = TabLegend[i];
691+ if( TabCtrl_InsertItem( PackageTabControl, i, &tab ) == -1 )
692+ {
693+ /* ...bailing out, and deleting the container window,
694+ * in the event of a creation error.
695+ */
696+ TabLegend[i + 1] = NULL;
697+ DestroyWindow( PackageTabControl );
698+ PackageTabControl = NULL;
699+ }
700+ }
701+ if( PackageTabControl != NULL )
702+ {
703+ /* When the tab control has been successfully created, we
704+ * create one additional basic SASH_WINDOW_PANE_CLASS window;
705+ * this serves to draw a border around the tab pane.
706+ */
707+ TabDataPane = new ChildWindowMaker( AppInstance );
708+ TabDataPane->Create( ID_PACKAGE_TABPANE, AppWindow, ClassName, WS_BORDER );
709+
710+ /* We also assign the package description data sheet as the
711+ * initial default tab selection.
712+ */
713+ TabCtrl_SetCurSel( PackageTabControl, PKG_DATASHEET_DESCRIPTION );
714+ }
715+}
716+
717+long AppWindowMaker::OnNotify( WPARAM client_id, LPARAM data )
718+{
719+ /* Handler for notifiable events to be processed in the context
720+ * of the main application window.
721+ *
722+ * FIXME: this supersedes the stub handler, originally provided
723+ * by pkgview.cpp; it may not yet be substantially complete, and
724+ * may eventually migrate elsewhere.
725+ */
726+ switch( client_id )
727+ {
728+ /* At present, we handle only mouse click events within the
729+ * package list view and data sheet tab control panes...
730+ */
731+ case ID_PACKAGE_LISTVIEW:
732+ case ID_PACKAGE_TABCONTROL:
733+ if( ((NMHDR *)(data))->code == NM_CLICK )
734+ /*
735+ * ...each of which may require the data sheet content
736+ * to be updated, (to reflect a changed selection).
737+ */
738+ DataSheet->DisplayData( PackageTabControl, PackageListView );
739+ break;
740+ }
741+ /* Otherwise, this return causes any other notifiable events
742+ * to be simply ignored, (as they were by the original stub).
743+ */
744+ return EXIT_SUCCESS;
745+}
746+
747+/* $RCSfile$: end of file */
--- /dev/null
+++ b/src/pkgdata.h
@@ -0,0 +1,60 @@
1+#ifndef PKGDATA_H
2+/*
3+ * pkgdata.h
4+ *
5+ * $Id$
6+ *
7+ * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
8+ * Copyright (C) 2010, 2012, MinGW Project
9+ *
10+ *
11+ * Declarations of the classes and properties used to implement the
12+ * package data sheet compilers, for both CLI and GUI clients.
13+ *
14+ *
15+ * This is free software. Permission is granted to copy, modify and
16+ * redistribute this software, under the provisions of the GNU General
17+ * Public License, Version 3, (or, at your option, any later version),
18+ * as published by the Free Software Foundation; see the file COPYING
19+ * for licensing details.
20+ *
21+ * Note, in particular, that this software is provided "as is", in the
22+ * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
23+ * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
24+ * PARTICULAR PURPOSE. Under no circumstances will the author, or the
25+ * MinGW Project, accept liability for any damages, however caused,
26+ * arising from the use of this software.
27+ *
28+ */
29+#define PKGDATA_H 1
30+
31+/* Utility classes and definitions for interpreting and formatting
32+ * package descriptions, encoded as UTF-8.
33+ */
34+typedef unsigned long utf32_t;
35+
36+#define UTF32_MAX (utf32_t)(0x10FFFFUL)
37+#define UTF32_OVERFLOW (utf32_t)(UTF32_MAX + 1UL)
38+#define UTF32_INVALID (utf32_t)(0x0FFFDUL)
39+
40+class pkgUTF8Parser
41+{
42+ /* A class for parsing a UTF-8 string, decomposing it into
43+ * non-whitespace substrings, separated by whitespace, which
44+ * is interpreted as potential word-wrap points.
45+ */
46+ public:
47+ pkgUTF8Parser( const char* );
48+ ~pkgUTF8Parser(){ delete next; }
49+
50+ protected:
51+ class pkgUTF8Parser *next;
52+ union { char utf8; wchar_t utf16; utf32_t utf32; } codepoint;
53+ const char *text;
54+ int length;
55+
56+ const char *ScanBuffer( const char* );
57+ wchar_t GetCodePoint( ... );
58+};
59+
60+#endif /* PKGDATA_H: $RCSfile$: end of file */
--- a/src/pkgshow.cpp
+++ b/src/pkgshow.cpp
@@ -30,6 +30,7 @@
3030 #include <stdarg.h>
3131
3232 #include "pkgbase.h"
33+#include "pkgdata.h"
3334 #include "pkgkeys.h"
3435 #include "pkglist.h"
3536
@@ -44,37 +45,10 @@
4445
4546 EXTERN_C const char *pkgMsgUnknownPackage( void );
4647
47-/* Utility classes and definitions for interpreting and formatting
48- * package descriptions, encoded as UTF-8.
49- */
50-typedef unsigned long utf32_t;
51-
52-#define UTF32_MAX (utf32_t)(0x10FFFFUL)
53-#define UTF32_OVERFLOW (utf32_t)(UTF32_MAX + 1UL)
54-
55-#define UTF32_INVALID (utf32_t)(0x0FFFDUL)
56-
57-class pkgUTF8Parser
58-{
59- /* A class for parsing a UTF-8 string, decomposing it into
60- * non-whitespace substrings, separated by whitespace, which
61- * is interpreted as potential word-wrap points.
62- */
63- public:
64- pkgUTF8Parser( const char* );
65- ~pkgUTF8Parser(){ delete next; }
66-
67- protected:
68- class pkgUTF8Parser *next;
69- union { char utf8; wchar_t utf16; utf32_t utf32; } codepoint;
70- const char *text;
71- int length;
72-
73- const char *ScanBuffer( const char* );
74- wchar_t GetCodePoint( ... );
75-};
76-
77-/* Constructor...
48+/* pkgUTF8Parser Class Implementation
49+ * ==================================
50+ *
51+ * Constructor...
7852 */
7953 pkgUTF8Parser::pkgUTF8Parser( const char *input ):
8054 text( NULL ), length( 0 ), next( NULL )
@@ -308,6 +282,9 @@ wchar_t pkgUTF8Parser::GetCodePoint( ... )
308282 return codepoint.utf16;
309283 }
310284
285+/* pkgNroffLayoutEngine Class Implementation (local)
286+ * =================================================
287+ */
311288 class pkgNroffLayoutEngine : public pkgUTF8Parser
312289 {
313290 /* A class for laying out a UTF-8 encoded string as a rudimentary
@@ -454,7 +431,10 @@ pkgNroffLayoutEngine::WriteLn( int style, int offset, int maxlen )
454431 return curr;
455432 }
456433
457-/* Constructor...
434+/* pkgDirectory Class Implementation
435+ * =================================
436+ *
437+ * Constructor...
458438 */
459439 pkgDirectory::pkgDirectory( pkgXmlNode *item ):
460440 entry( item ), prev( NULL ), next( NULL ){}
@@ -583,6 +563,9 @@ pkgDirectory::~pkgDirectory()
583563 delete next;
584564 }
585565
566+/* pkgDirectoryViewer Class Implementation
567+ * =======================================
568+ */
586569 class pkgDirectoryViewer : public pkgDirectoryViewerEngine
587570 {
588571 /* A concrete class, providing the directory traversal hooks
--- a/src/pkgview.cpp
+++ b/src/pkgview.cpp
@@ -132,6 +132,12 @@ long AppWindowMaker::OnCommand( WPARAM cmd )
132132 return EXIT_SUCCESS;
133133 }
134134
135+#if 0
136+/* FIXME: this stub implementation has been superseded by an
137+ * alternative implementation in pkgdata.cpp; eventually, this
138+ * may itself be superseded, and may migrate elsewhere, perhaps
139+ * even back to here.
140+ */
135141 long AppWindowMaker::OnNotify( WPARAM client_id, LPARAM data )
136142 {
137143 /* Handler for notifications received from user controls; for now
@@ -139,6 +145,7 @@ long AppWindowMaker::OnNotify( WPARAM client_id, LPARAM data )
139145 */
140146 return EXIT_SUCCESS;
141147 }
148+#endif
142149
143150 long AppWindowMaker::OnSize( WPARAM mode, int width, int height )
144151 {
@@ -216,6 +223,44 @@ int AppWindowMaker::LayoutEngine( HWND pane, LPARAM region )
216223 pane_width -= pane_left;
217224 break;
218225
226+ case ID_PACKAGE_TABPANE:
227+ case ID_PACKAGE_TABCONTROL:
228+ case ID_PACKAGE_DATASHEET:
229+ /* Lower right hand pane; occupies the remaining area of the parent,
230+ * to the right of the tree view pane, and below the list view, again
231+ * with allowance for small gaps to the left and above, to accommodate
232+ * the sash bars separating it from the adjacent panes.
233+ */
234+ pane_top = VerticalSash->Displacement( pane_height ) + SASH_BAR_THICKNESS;
235+ pane_left = HorizontalSash->Displacement( pane_width ) + SASH_BAR_THICKNESS;
236+ if( pane_id == ID_PACKAGE_TABCONTROL )
237+ {
238+ /* Shift the tab control window, and shrink it, so that it doesn't
239+ * occlude the border surrounding the underlying tab pane window.
240+ */
241+ ++pane_left; ++pane_top; --pane_width; --pane_height;
242+ }
243+ else if( pane_id == ID_PACKAGE_DATASHEET )
244+ {
245+ /* Leave the data sheet window at the full width of the tab pane,
246+ * but adjust its height and top edge position so that it appears
247+ * below to tabs; (its own border will appear in place of the tab
248+ * pane border, where they overlap.
249+ */
250+ RECT frame;
251+ frame.top = pane_top;
252+ frame.left = pane_left;
253+ frame.right = pane_left + pane_width;
254+ frame.bottom = pane_top + pane_height;
255+ TabCtrl_AdjustRect( PackageTabControl, FALSE, &frame );
256+ pane_top = frame.top;
257+ }
258+ /* Adjust height and width to fill the space below and to the right
259+ * of the two sash bars.
260+ */
261+ pane_height -= pane_top; pane_width -= pane_left;
262+ break;
263+
219264 case ID_HORIZONTAL_SASH:
220265 /* A slim window, placed to the right of the tree view pane, and
221266 * occupying the full height of the parent window, representing the