The MinGW.org Installation Manager Tool
Revision | 95551e342c568545e2a60ec2e82304ba1291799c (tree) |
---|---|
Time | 2012-10-11 19:02:17 |
Author | Keith Marshall <keithmarshall@user...> |
Commiter | Keith Marshall |
Implement preliminary GUI mode data sheet compiler.
@@ -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 | + | |
1 | 35 | 2012-10-09 Keith Marshall <keithmarshall@users.sourceforge.net> |
2 | 36 | |
3 | 37 | Remove a grossly heuristic list view hack. |
@@ -91,7 +91,7 @@ CLI_EXE_OBJECTS = \ | ||
91 | 91 | |
92 | 92 | GUIMAIN_OBJECTS = \ |
93 | 93 | guimain.$(OBJEXT) guidata.$(OBJEXT) guixmld.$(OBJEXT) guidmh.$(OBJEXT) \ |
94 | - approot.$(OBJEXT) pkgview.$(OBJEXT) pkglist.$(OBJEXT) | |
94 | + approot.$(OBJEXT) pkgview.$(OBJEXT) pkglist.$(OBJEXT) pkgdata.$(OBJEXT) | |
95 | 95 | |
96 | 96 | GUIMAIN_LIBS = -lwtklite -lcomctl32 |
97 | 97 |
@@ -59,6 +59,7 @@ STRINGTABLE DISCARDABLE | ||
59 | 59 | BEGIN |
60 | 60 | ID_MAIN_WINDOW_CLASS "mingw-get-gui" |
61 | 61 | ID_MAIN_WINDOW_CAPTION "MinGW Installation Manager" |
62 | + ID_SASH_WINDOW_PANE_CLASS "mingw-get-sash-pane" | |
62 | 63 | ID_FONT_PREF "Verdana" |
63 | 64 | END |
64 | 65 |
@@ -35,6 +35,7 @@ | ||
35 | 35 | #define ID_MAIN_WINDOW_CAPTION 102 |
36 | 36 | #define ID_FONT_PREF 103 |
37 | 37 | |
38 | +#define ID_SASH_WINDOW_PANE_CLASS 104 | |
38 | 39 | #define ID_HORIZONTAL_SASH 105 |
39 | 40 | #define ID_VERTICAL_SASH 106 |
40 | 41 |
@@ -43,6 +44,8 @@ | ||
43 | 44 | #define ID_PACKAGE_TREEVIEW 201 |
44 | 45 | #define ID_PACKAGE_LISTVIEW 202 |
45 | 46 | #define ID_PACKAGE_TABCONTROL 203 |
47 | +#define ID_PACKAGE_DATASHEET 204 | |
48 | +#define ID_PACKAGE_TABPANE 205 | |
46 | 49 | |
47 | 50 | #define IDM_MAIN_MENU 300 |
48 | 51 | #define IDM_REPO_UPDATE 301 |
@@ -96,6 +99,7 @@ | ||
96 | 99 | #include <commctrl.h> |
97 | 100 | |
98 | 101 | class pkgXmlDocument; |
102 | +class DataSheetMaker; | |
99 | 103 | |
100 | 104 | class AppWindowMaker; |
101 | 105 | inline AppWindowMaker *GetAppWindow( HWND lookup ) |
@@ -137,6 +141,11 @@ class AppWindowMaker: public WTK::MainWindowMaker | ||
137 | 141 | void InitPackageListView( void ); |
138 | 142 | void ClearPackageList( void ){ ListView_DeleteAllItems( PackageListView ); } |
139 | 143 | void UpdatePackageList( void ); |
144 | + | |
145 | + DataSheetMaker *DataSheet; | |
146 | + WTK::ChildWindowMaker *TabDataPane; | |
147 | + HWND PackageTabControl, PackageTabPane; | |
148 | + void InitPackageTabControl(); | |
140 | 149 | }; |
141 | 150 | |
142 | 151 | #endif /* ! RC_INVOKED */ |
@@ -50,6 +50,11 @@ int AppWindowMaker::Invoked( void ) | ||
50 | 50 | LoadPackageData(); |
51 | 51 | InitPackageListView(); |
52 | 52 | |
53 | + /* Initialise the data-sheet tab control, displaying the default | |
54 | + * "no package selected" message. | |
55 | + */ | |
56 | + InitPackageTabControl(); | |
57 | + | |
53 | 58 | /* Force a layout adjustment, to ensure that the displayed |
54 | 59 | * data controls are correctly populated. |
55 | 60 | */ |
@@ -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 */ |
@@ -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 */ |
@@ -30,6 +30,7 @@ | ||
30 | 30 | #include <stdarg.h> |
31 | 31 | |
32 | 32 | #include "pkgbase.h" |
33 | +#include "pkgdata.h" | |
33 | 34 | #include "pkgkeys.h" |
34 | 35 | #include "pkglist.h" |
35 | 36 |
@@ -44,37 +45,10 @@ | ||
44 | 45 | |
45 | 46 | EXTERN_C const char *pkgMsgUnknownPackage( void ); |
46 | 47 | |
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... | |
78 | 52 | */ |
79 | 53 | pkgUTF8Parser::pkgUTF8Parser( const char *input ): |
80 | 54 | text( NULL ), length( 0 ), next( NULL ) |
@@ -308,6 +282,9 @@ wchar_t pkgUTF8Parser::GetCodePoint( ... ) | ||
308 | 282 | return codepoint.utf16; |
309 | 283 | } |
310 | 284 | |
285 | +/* pkgNroffLayoutEngine Class Implementation (local) | |
286 | + * ================================================= | |
287 | + */ | |
311 | 288 | class pkgNroffLayoutEngine : public pkgUTF8Parser |
312 | 289 | { |
313 | 290 | /* 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 ) | ||
454 | 431 | return curr; |
455 | 432 | } |
456 | 433 | |
457 | -/* Constructor... | |
434 | +/* pkgDirectory Class Implementation | |
435 | + * ================================= | |
436 | + * | |
437 | + * Constructor... | |
458 | 438 | */ |
459 | 439 | pkgDirectory::pkgDirectory( pkgXmlNode *item ): |
460 | 440 | entry( item ), prev( NULL ), next( NULL ){} |
@@ -583,6 +563,9 @@ pkgDirectory::~pkgDirectory() | ||
583 | 563 | delete next; |
584 | 564 | } |
585 | 565 | |
566 | +/* pkgDirectoryViewer Class Implementation | |
567 | + * ======================================= | |
568 | + */ | |
586 | 569 | class pkgDirectoryViewer : public pkgDirectoryViewerEngine |
587 | 570 | { |
588 | 571 | /* A concrete class, providing the directory traversal hooks |
@@ -132,6 +132,12 @@ long AppWindowMaker::OnCommand( WPARAM cmd ) | ||
132 | 132 | return EXIT_SUCCESS; |
133 | 133 | } |
134 | 134 | |
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 | + */ | |
135 | 141 | long AppWindowMaker::OnNotify( WPARAM client_id, LPARAM data ) |
136 | 142 | { |
137 | 143 | /* Handler for notifications received from user controls; for now |
@@ -139,6 +145,7 @@ long AppWindowMaker::OnNotify( WPARAM client_id, LPARAM data ) | ||
139 | 145 | */ |
140 | 146 | return EXIT_SUCCESS; |
141 | 147 | } |
148 | +#endif | |
142 | 149 | |
143 | 150 | long AppWindowMaker::OnSize( WPARAM mode, int width, int height ) |
144 | 151 | { |
@@ -216,6 +223,44 @@ int AppWindowMaker::LayoutEngine( HWND pane, LPARAM region ) | ||
216 | 223 | pane_width -= pane_left; |
217 | 224 | break; |
218 | 225 | |
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 | + | |
219 | 264 | case ID_HORIZONTAL_SASH: |
220 | 265 | /* A slim window, placed to the right of the tree view pane, and |
221 | 266 | * occupying the full height of the parent window, representing the |