• R/O
  • HTTP
  • SSH
  • HTTPS

コミット

タグ
未設定

よく使われているワード(クリックで追加)

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

The MinGW.org Installation Manager Tool


コミットメタ情報

リビジョン2a0474b5df077959fd7ca149eefcd9dc4af61e1e (tree)
日時2013-07-15 06:13:50
作者Keith Marshall <keithmarshall@user...>
コミッターKeith Marshall

ログメッセージ

Implement GUI filtering of package list by group selection.

変更サマリ

差分

--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,42 @@
1+2013-07-14 Keith Marshall <keithmarshall@users.sourceforge.net>
2+
3+ Implement GUI filtering of package list by group selection.
4+
5+ * src/pkgbase.h (pkgXmlNode::IsVisibleGroupMember):
6+ (pkgXmlNode::IsVisibleClass): New inline methods; declare them.
7+ (pkgXmlNode::MapPackageGroupHierarchy): New inline method; declare it.
8+ (pkgXmlNode::SetPackageGroupHierarchyMapper): Likewise; it sets up...
9+ (pkgXmlNode::PackageGroupHierarchyMapper): ...this new private static
10+ function pointer; declare it as a function reference, in terms of...
11+ (pkgXmlNode::GroupHierarchyMapper): ...this new typedef; define it.
12+
13+ * src/guimain.h (AppWindowMaker::PackageTreeView): Make it static.
14+ (AppWindowMaker::IsPackageGroupAffiliate): Declare new static method.
15+
16+ * src/pkgbind.cpp (pkgRepository::GetPackageList): Make it invoke...
17+ (pkgXmlNode::MapPackageGroupHierarchy): ...this; implement it.
18+
19+ * src/pkglist.cpp (pkgXmlNode::IsVisibleGroupMember): Implement it.
20+ (pkgXmlNode::IsVisibleClass): Likewise; just provide a stub, for now.
21+ (pkgListViewMaker::Dispatch): Use them to filter package list content.
22+ (AppWindowMaker::UpdatePackageList): Redraw main window when done.
23+
24+ * src/pkgtree.cpp [_WIN32_IE >= 0x0400]: Require this.
25+ (AppWindowMaker::PackageTreeView): Define and initialise it.
26+ (AppWindowMaker::InitPackageTreeView): Extend implementation; use...
27+ (map_package_group_hierarchy_recursive, load package_group_hierarchy):
28+ (is_existing_group, map_package_group_hierarchy): ...these; implement
29+ them locally, as file scoped static helper functions.
30+ (select_key): New static local string constant; define it.
31+ (pkgInitCategoryTreeGraft): Use it; also have it invoke...
32+ (pkgXmlNode::SetPackageGroupHierarchyMapper): ...this; implement it.
33+ (AppWindowMaker::IsPackageGroupAffiliate): Implement it; it uses...
34+ (is_affiliated, is_child_affiliate): ...these helpers; provide them as
35+ locally implemented, file scoped static functions.
36+
37+ * src/pkgdata.cpp (AppWindowMaker::OnNotify): Add handler for...
38+ [ID_PACKAGE_TREEVIEW && TVN_SELCHANGED]: ...this notification.
39+
140 2013-07-12 Keith Marshall <keithmarshall@users.sourceforge.net>
241
342 Correct a static string buffer aliasing issue.
--- a/src/guimain.h
+++ b/src/guimain.h
@@ -185,6 +185,7 @@ class AppWindowMaker: public WTK::MainWindowMaker
185185 int DispatchDialogueThread( int, pkgDialogueThread * );
186186
187187 void LoadPackageData( bool = false );
188+ static bool IsPackageGroupAffiliate( pkgXmlNode * );
188189 void ClearPackageList( void ){ ListView_DeleteAllItems( PackageListView ); }
189190 void UpdatePackageList( void );
190191
@@ -210,7 +211,7 @@ class AppWindowMaker: public WTK::MainWindowMaker
210211 pkgProgressMeter *AttachedProgressMeter;
211212 HFONT DefaultFont;
212213
213- HWND PackageTreeView;
214+ static HWND PackageTreeView;
214215 void InitPackageTreeView( void );
215216
216217 HWND PackageListView;
--- a/src/pkgbase.h
+++ b/src/pkgbase.h
@@ -151,12 +151,28 @@ class pkgXmlNode : public TiXmlElement
151151 return this ? strcmp( GetName(), tagname ) == 0 : false;
152152 }
153153
154+ /* Methods to determine which packages should be displayed
155+ * in the package list pane of the GUI client.
156+ */
157+ inline bool IsVisibleGroupMember();
158+ inline bool IsVisibleClass();
159+
154160 /* Methods for retrieving the system root management records
155161 * for a specified installed subsystem.
156162 */
157163 pkgXmlNode *GetSysRoot( const char* );
158164 pkgXmlNode *GetInstallationRecord( const char* );
159165
166+ /* Methods for mapping the package group hierarchy.
167+ */
168+ inline void SetPackageGroupHierarchyMapper();
169+ inline void MapPackageGroupHierarchy( pkgXmlNode* );
170+
171+ /* Type definition for a helper function, which must be assigned
172+ * to the package group hierarchy mapper, in order to enable it.
173+ */
174+ typedef void (*GroupHierarchyMapper)( pkgXmlNode*, pkgXmlNode* );
175+
160176 /* The following pair of methods provide an iterator
161177 * for enumerating the contained nodes, within the owner,
162178 * which themselves exhibit a specified tagname.
@@ -195,6 +211,11 @@ class pkgXmlNode : public TiXmlElement
195211 */
196212 int InvokeScript( int, const char* );
197213 int DispatchScript( int, const char*, const char*, pkgXmlNode* );
214+
215+ /* Hook via which the requisite helper function is attached
216+ * to the package group hierarchy mapper.
217+ */
218+ static GroupHierarchyMapper PackageGroupHierarchyMapper;
198219 };
199220
200221 enum { to_remove = 0, to_install, selection_types };
--- a/src/pkgbind.cpp
+++ b/src/pkgbind.cpp
@@ -83,6 +83,21 @@ pkgRepository::pkgRepository
8383 owner( client ), dbase( db ), repository( ref ), force_update( mode ),
8484 expected_issue( value_assumed_new ){}
8585
86+/* Provide the hook, via which the package group hierarchy builder
87+ * may gain access to its configuration data, during loading of the
88+ * package list files.
89+ */
90+pkgXmlNode::GroupHierarchyMapper
91+pkgXmlNode::PackageGroupHierarchyMapper = NULL;
92+inline void pkgXmlNode::MapPackageGroupHierarchy( pkgXmlNode *catalogue )
93+{
94+ /* This is a no-op, unless the client attaches a handler to the
95+ * hook, before invoking the package list loader.
96+ */
97+ if( PackageGroupHierarchyMapper != NULL )
98+ PackageGroupHierarchyMapper( this, catalogue );
99+}
100+
86101 void pkgRepository::GetPackageList( const char *dname )
87102 {
88103 /* Helper to retrieve and recursively process a named package list.
@@ -179,7 +194,11 @@ void pkgRepository::GetPackageList( const char *dname )
179194 pkgXmlNode *catalogue, *pkglist;
180195 if( (catalogue = merge.GetRoot()) != NULL )
181196 {
182- /* ...read it, selecting each of the "package-collection"
197+ /* ...map any package group hierarchy which it specifies...
198+ */
199+ dbase->MapPackageGroupHierarchy( catalogue );
200+
201+ /* ...then read it, selecting each of the "package-collection"
183202 * records contained within it...
184203 */
185204 pkglist = catalogue->FindFirstAssociate( package_collection_key );
--- a/src/pkgdata.cpp
+++ b/src/pkgdata.cpp
@@ -1328,6 +1328,66 @@ long AppWindowMaker::OnNotify( WPARAM client_id, LPARAM data )
13281328 SelectPackageAction( LVHT_ONITEMICON );
13291329 }
13301330 break;
1331+
1332+ /* We also need to consider notifications from the tree view...
1333+ */
1334+ case ID_PACKAGE_TREEVIEW:
1335+ if( ((NMHDR *)(data))->code == TVN_SELCHANGED )
1336+ {
1337+ /* ...from which we are interested only in notifications
1338+ * that the user has changed the package group selection.
1339+ *
1340+ * First, we ensure that any children of the selected
1341+ * package group are made visible.
1342+ */
1343+ TreeView_Expand( PackageTreeView,
1344+ ((NMTREEVIEW *)(data))->itemNew.hItem, TVE_EXPAND
1345+ );
1346+
1347+ /* We then clear out the previous content of the list view
1348+ * pane, and reconstruct it with new content, as determined
1349+ * by the new package group selection...
1350+ */
1351+ ClearPackageList();
1352+ UpdatePackageList();
1353+
1354+ /* ...and reapply any scheduled action markers, which may
1355+ * be applicable.
1356+ */
1357+ MarkSchedule( pkgData->Schedule() );
1358+
1359+ /* Finally, provided the previous selection is not an
1360+ * ancestor of the current, we may collapse any visible
1361+ * subtree descending from the previous.
1362+ *
1363+ * FIXME: We may wish to avoid collapsing any subtree
1364+ * which was designated as "expanded", in the original
1365+ * group hierarchy specification. We may also wish to
1366+ * provide a user option, to disable this feature.
1367+ */
1368+ bool may_fold = true;
1369+ HTREEITEM prev = ((NMTREEVIEW *)(data))->itemNew.hItem;
1370+ while( may_fold && (prev != NULL) )
1371+ { if( prev == ((NMTREEVIEW *)(data))->itemOld.hItem )
1372+ /*
1373+ * Previous selection IS an ancestor of current;
1374+ * we must not collapse it.
1375+ */
1376+ may_fold = false;
1377+
1378+ else
1379+ /* Continue tracing ancestry, back to the root.
1380+ */
1381+ prev = TreeView_GetParent( PackageTreeView, prev );
1382+ }
1383+ if( may_fold )
1384+ /*
1385+ * Previous selection may be collapsed; do so.
1386+ */
1387+ TreeView_Expand( PackageTreeView,
1388+ ((NMTREEVIEW *)(data))->itemOld.hItem, TVE_COLLAPSE
1389+ );
1390+ }
13311391 }
13321392 /* Otherwise, this return causes any other notifiable events
13331393 * to be simply ignored, (as they were by the original stub).
--- a/src/pkglist.cpp
+++ b/src/pkglist.cpp
@@ -113,8 +113,24 @@ void AppWindowMaker::UpdatePackageList()
113113 pkgDirectory *dir = pkgData->CatalogueAllPackages();
114114 dir->InOrder( &PackageList );
115115 delete dir;
116+
117+ /* Force a redraw of the application window, to ensure that the
118+ * data pane content remains synchronised.
119+ */
120+ InvalidateRect( AppWindow, NULL, FALSE );
121+ UpdateWindow( AppWindow );
116122 }
117123
124+inline bool pkgXmlNode::IsVisibleGroupMember()
125+{
126+ /* Hook invoked before adding a package reference to the package list,
127+ * to ensure that it represents a member of the current package group.
128+ */
129+ return AppWindowMaker::IsPackageGroupAffiliate( this );
130+}
131+
132+inline bool pkgXmlNode::IsVisibleClass(){ return true; }
133+
118134 static char *pkgGetTitle( pkgXmlNode *pkg, const pkgXmlNode *xml_root )
119135 {
120136 /* A private helper method, to retrieve the title attribute
@@ -421,7 +437,7 @@ void pkgListViewMaker::Dispatch( pkgXmlNode *package )
421437 * dispatching the content of the directory to the display service,
422438 * (which, in this case, populates the list view window pane).
423439 */
424- if( package->IsElementOfType( package_key ) )
440+ if( package->IsElementOfType( package_key ) && package->IsVisibleGroupMember() )
425441 {
426442 /* Assemble the package name into the list view record block.
427443 */
@@ -445,7 +461,7 @@ void pkgListViewMaker::Dispatch( pkgXmlNode *package )
445461 */
446462 InsertItem( package, (char *)("") );
447463 }
448- else if( package->IsElementOfType( component_key ) )
464+ else if( package->IsElementOfType( component_key ) && package->IsVisibleClass() )
449465 {
450466 /* Handle the recursive calls for the component sub-directory,
451467 * inheriting the package name entry from the original package
--- a/src/pkgtree.cpp
+++ b/src/pkgtree.cpp
@@ -4,7 +4,7 @@
44 * $Id$
55 *
66 * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7- * Copyright (C) 2012, MinGW.org Project
7+ * Copyright (C) 2012, 2013, MinGW.org Project
88 *
99 *
1010 * Implementation of the methods for the pkgTreeViewMaker class, and
@@ -26,6 +26,8 @@
2626 * arising from the use of this software.
2727 *
2828 */
29+#define _WIN32_IE 0x0400
30+
2931 #include "guimain.h"
3032 #include "pkgbase.h"
3133 #include "pkgkeys.h"
@@ -36,9 +38,186 @@ static const char *package_group_all = "All Packages";
3638 /* The following are candidates for inclusion in "pkgkeys";
3739 * for now, we may keep them as local defines.
3840 */
41+static const char *select_key = "select";
3942 static const char *expand_key = "expand";
4043 static const char *value_true = "true";
4144
45+static inline
46+pkgXmlNode *is_existing_group( pkgXmlNode *dbase, const char *name )
47+{
48+ /* Helper to check for prior existence of a specified group name,
49+ * at a specified level within the XML representation of the package
50+ * group hierarchy, so we may avoid adding redundant duplicates.
51+ */
52+ while( dbase != NULL )
53+ {
54+ /* We haven't yet considered all XML database entries, at the
55+ * requisite level; check the current reference entry...
56+ */
57+ if( safe_strcmp( strcasecmp, name, dbase->GetPropVal( name_key, NULL )) )
58+ /*
59+ * ...returning it immediately, if it is a named duplicate of
60+ * the entry we are seeking to add.
61+ */
62+ return dbase;
63+
64+ /* When no duplicate found yet, move on to consider the next
65+ * entry at the requisite XML database level, if any.
66+ */
67+ dbase = dbase->FindNextAssociate( package_group_key );
68+ }
69+ /* If we get to here, then no duplicate was found; the database
70+ * reference must have become NULL, which we return.
71+ */
72+ return dbase;
73+}
74+
75+static void
76+map_package_group_hierarchy_recursive( pkgXmlNode *dbase, pkgXmlNode *group )
77+{
78+ /* Helper to recursively reconstruct an in-core image of the XML
79+ * structure of the package list hierarchy, as it is specified in
80+ * the package list read from external storage.
81+ */
82+ const char *name = group->GetPropVal( name_key, value_unknown );
83+ pkgXmlNode *ref, *map = dbase->FindFirstAssociate( package_group_key );
84+ if( (ref = is_existing_group( map, name )) == NULL )
85+ {
86+ /* The group name we are seeking to add doesn't yet appear at
87+ * the requisite level within the in-core XML image; construct
88+ * a new record, comprising the relevant data as represented
89+ * in the external database...
90+ */
91+ ref = new pkgXmlNode( package_group_key );
92+ ref->SetAttribute( name_key, name );
93+ if( (name = group->GetPropVal( expand_key, NULL )) != NULL )
94+ ref->SetAttribute( expand_key, name );
95+
96+ /* ...and attach it as the last sibling of any existing
97+ * in-core records at the requisite level.
98+ */
99+ ref = (pkgXmlNode *)(dbase->LinkEndChild( ref ));
100+ }
101+ /* Recurse...
102+ */
103+ group = group->FindFirstAssociate( package_group_key );
104+ while( group != NULL )
105+ {
106+ /* ...to capture any newly specified children of this group,
107+ * regardless of whether the group already existed, or has
108+ * just been added.
109+ */
110+ map_package_group_hierarchy_recursive( ref, group );
111+ group = group->FindNextAssociate( package_group_key );
112+ }
113+}
114+
115+static void
116+map_package_group_hierarchy( pkgXmlNode *dbase, pkgXmlNode *catalogue )
117+{
118+ /* Helper to kick-start the recursive mapping of external XML
119+ * records into the in-core package group hierarchy image.
120+ */
121+ const char *group_hierarchy_key = "package-group-hierarchy";
122+ if( (catalogue = catalogue->FindFirstAssociate( group_hierarchy_key )) != NULL )
123+ {
124+ /* We found a package group hierarchy specification...
125+ */
126+ dbase = dbase->FindFirstAssociate( package_group_key );
127+ do { pkgXmlNode *group = catalogue->FindFirstAssociate( package_group_key );
128+ while( group != NULL )
129+ {
130+ /* ...recursively map each top level package group
131+ * which is specified within it...
132+ */
133+ map_package_group_hierarchy_recursive( dbase, group );
134+ group = group->FindNextAssociate( package_group_key );
135+ }
136+ /* ...and repeat for any other package group hierarchy
137+ * specifications we may encounter.
138+ */
139+ catalogue = catalogue->FindNextAssociate( group_hierarchy_key );
140+ } while( catalogue != NULL );
141+ }
142+}
143+
144+static void
145+load_package_group_hierarchy( HWND display, HTREEITEM parent, pkgXmlNode *group )
146+{
147+ /* Helper to load the package group hierarchy from it's in-core XML
148+ * database representation, into a Windows tree view control.
149+ */
150+ TVINSERTSTRUCT ref;
151+
152+ /* Establish initial state for the tree view item insertion control.
153+ */
154+ ref.hParent = parent;
155+ ref.item.mask = TVIF_TEXT | TVIF_CHILDREN;
156+ ref.hInsertAfter = TVI_FIRST;
157+
158+ do { /* For each package group specified at the current level in the
159+ * package group hierarchy, retrieve its name from the XML record...
160+ */
161+ pkgXmlNode *first_child = group->FindFirstAssociate( package_group_key );
162+ ref.item.pszText = (char *)(group->GetPropVal( name_key, value_unknown ));
163+ /*
164+ * ...note whether any descendants are to be specified...
165+ */
166+ ref.item.cChildren = (first_child == NULL) ? 0 : 1;
167+ /*
168+ * ...and add a corresponding entry to the tree view.
169+ */
170+ ref.hInsertAfter = TreeView_InsertItem( display, &ref );
171+
172+ /* Check if the current group is to be made the initial selection,
173+ * when the tree view is first displayed; the last entry so marked
174+ * will become the actual initial selection. (Note that, to ensure
175+ * that this setting cannot be abused by package maintainers, it is
176+ * defined internally by mingw-get; it is not propagated from any
177+ * external XML resource).
178+ */
179+ const char *option = group->GetPropVal( select_key, NULL );
180+ if( (option != NULL) && (strcmp( option, value_true ) == 0) )
181+ /*
182+ * Make any group, which marked with the "select" attribute, the
183+ * active selection; (note that this selection may be superseded,
184+ * should another similarly marked group be encountered later).
185+ */
186+ TreeView_SelectItem( display, ref.hInsertAfter );
187+
188+ /* Any descendants specified, for the current group, must be
189+ * processed recursively...
190+ */
191+ if( first_child != NULL )
192+ {
193+ /* There is at least one generation of children, of the current
194+ * tree view entry; check if this entry should be expanded, so as
195+ * to make its children visible in the initial view, recursively
196+ * evaluate to identify further generations of descendants...
197+ */
198+ option = group->GetPropVal( expand_key, NULL );
199+ load_package_group_hierarchy( display, ref.hInsertAfter, first_child );
200+ if( (option != NULL) && (strcmp( option, value_true ) == 0) )
201+ /*
202+ * ...and expand to the requested level of visibility.
203+ */
204+ TreeView_Expand( display, ref.hInsertAfter, TVE_EXPAND );
205+ }
206+ /* Repeat for any other package groups which may be specified at
207+ * the current level within the hierarchy.
208+ */
209+ group = group->FindNextAssociate( package_group_key );
210+ } while( group != NULL );
211+}
212+
213+inline void pkgXmlNode::SetPackageGroupHierarchyMapper()
214+{
215+ /* Method to assign the preceding helper, as the active handler
216+ * for pkgXmlNode::MapPackageGroupHierarchy().
217+ */
218+ PackageGroupHierarchyMapper = map_package_group_hierarchy;
219+}
220+
42221 EXTERN_C void pkgInitCategoryTreeGraft( pkgXmlNode *root )
43222 {
44223 /* Helper function to create the graft point, at which the
@@ -47,7 +226,10 @@ EXTERN_C void pkgInitCategoryTreeGraft( pkgXmlNode *root )
47226 * of the XML database image.
48227 */
49228 pkgXmlNode *pkgtree = new pkgXmlNode( package_group_key );
229+
230+ pkgtree->SetPackageGroupHierarchyMapper();
50231 pkgtree->SetAttribute( name_key, package_group_all );
232+ pkgtree->SetAttribute( select_key, value_true );
51233 pkgtree->SetAttribute( expand_key, value_true );
52234 root->LinkEndChild( pkgtree );
53235 }
@@ -57,8 +239,8 @@ void AppWindowMaker::InitPackageTreeView()
57239 /* Create and initialise a TreeView window, in which to present
58240 * the package group hierarchy display...
59241 */
60- PackageTreeView = CreateWindow( WC_TREEVIEW, NULL,
61- WS_VISIBLE | WS_BORDER | WS_CHILD, 0, 0, 0, 0,
242+ PackageTreeView = CreateWindow( WC_TREEVIEW, NULL, WS_VISIBLE |
243+ WS_BORDER | WS_CHILD | TVS_FULLROWSELECT | TVS_SHOWSELALWAYS, 0, 0, 0, 0,
62244 AppWindow, (HMENU)(ID_PACKAGE_TREEVIEW),
63245 AppInstance, NULL
64246 );
@@ -67,14 +249,7 @@ void AppWindowMaker::InitPackageTreeView()
67249 * displaying the category headings within the tree view.
68250 */
69251 SendMessage( PackageTreeView, WM_SETFONT, (WPARAM)(DefaultFont), TRUE );
70-
71- /* Initialise a tree view insertion structure, to the appropriate
72- * state for assignment of the root entry in the tree view.
73- */
74- TVINSERTSTRUCT cat;
75- cat.hParent = TVI_ROOT;
76- cat.item.mask = TVIF_TEXT;
77- cat.hInsertAfter = TVI_ROOT;
252+ SendMessage( PackageTreeView, TVM_SETINDENT, 0, 0 );
78253
79254 /* Retrieve the root category entry, if any, as recorded in
80255 * the package XML database, for assignment as the root entry
@@ -87,23 +262,181 @@ void AppWindowMaker::InitPackageTreeView()
87262 * create an artificial root entry, (which will then become
88263 * the sole entry in our tree view).
89264 */
90- cat.item.pszText = (char *)(package_group_all);
91- TreeView_InsertItem( PackageTreeView, &cat );
265+ TVINSERTSTRUCT entry;
266+ entry.hParent = TVI_ROOT;
267+ entry.item.mask = TVIF_TEXT | TVIF_CHILDREN;
268+ entry.item.pszText = (char *)(package_group_all);
269+ entry.hInsertAfter = TVI_ROOT;
270+ entry.item.cChildren = 0;
271+
272+ /* Map this sole entry into the tree view pane.
273+ */
274+ TreeView_InsertItem( PackageTreeView, &entry );
92275 }
93- else while( tree != NULL )
94- {
276+ else
95277 /* The package group hierarchy has been incorporated into
96278 * the in-core image of the XML database; create a windows
97- * "tree view" representation of its structure.
98- *
99- * FIXME: this currently creates only the root of the tree;
100- * we need to walk the XML hierarchy, and add an additional
101- * tree view node for each element found.
279+ * "tree view", into which we load a representation of the
280+ * structure of this hierarchy.
281+ */
282+ load_package_group_hierarchy( PackageTreeView, TVI_ROOT, tree );
283+}
284+
285+static bool is_child_affiliate( HWND tree, TVITEM *ref, const char *name )
286+{
287+ /* Helper method to recursively traverse a subtree of the
288+ * package group hierarchy, to check if a specified package
289+ * group name matches any descendant of the current group
290+ * selection...
291+ */
292+ HTREEITEM mark = ref->hItem;
293+ do { if( TreeView_GetItem( tree, ref )
294+ && (strcasecmp( name, ref->pszText ) == 0) )
295+ /*
296+ * ...immediately promoting any such match as an
297+ * affiliate of the current selection...
298+ */
299+ return true;
300+
301+ /* ...otherwise, recursively traverse each child
302+ * subtree, at the current level...
303+ */
304+ if( ((ref->hItem = TreeView_GetChild( tree, mark )) != NULL)
305+ && is_child_affiliate( tree, ref, name ) )
306+ /*
307+ * ...again, immediately promoting any match...
308+ */
309+ return true;
310+
311+ /* ...or otherwise, rewind to the marked match point,
312+ * whence we repeat the search into its next immediate
313+ * sibling subtree, (if any).
314+ */
315+ mark = ref->hItem = TreeView_GetNextSibling( tree, mark );
316+ } while( mark != NULL );
317+
318+ /* If we get to here, the specified package group name is NOT
319+ * an affiliate of any descendant, at the current match level,
320+ * of the currently selected package group.
321+ */
322+ return false;
323+}
324+
325+static inline bool is_affiliated( HWND tree, const char *name )
326+{
327+ /* Helper to initiate a determination if a specified package
328+ * group name is an affiliate of the currently selected package
329+ * group; i.e. if it matches the name of the selected group, or
330+ * any of its descendants.
331+ */
332+ TVITEM ref;
333+ if( ((ref.hItem = TreeView_GetSelection( tree )) == NULL)
334+ || (ref.hItem == TreeView_GetRoot( tree )) )
335+ /*
336+ * Before proceeding further, we may note that ANY group name
337+ * is considered to be IMPLICITLY matched, at the root of the
338+ * package group tree.
339+ */
340+ return true;
341+
342+ /* At any level in the hierarchy, other than the root, the group
343+ * name MUST be matched EXPLICITLY; note that...
344+ */
345+ if( name == NULL )
346+ /* ...an unspecified name can never satisfy this criterion.
102347 */
103- cat.item.pszText = (char *)(tree->GetPropVal( name_key, value_unknown ));
104- HTREEITEM top = TreeView_InsertItem( PackageTreeView, &cat );
105- tree = tree->FindNextAssociate( package_group_key );
348+ return false;
349+
350+ /* As a basis for comparison, we must provide a working buffer
351+ * into which we may retrieve names from the tree view...
352+ */
353+ char ref_text[ref.cchTextMax = 1 + strlen( name )];
354+
355+ /* ...beginning with the currently selected tree view item...
356+ */
357+ ref.mask = TVIF_TEXT;
358+ ref.pszText = ref_text;
359+ if( TreeView_GetItem( tree, &ref ) && (strcasecmp( name, ref_text ) == 0) )
360+ /*
361+ * ...and returning immediately, when it is found to match.
362+ */
363+ return true;
364+
365+ /* When the selected item doesn't match, we also look for a
366+ * possible match among its descendants, if any...
367+ */
368+ if( (ref.hItem = TreeView_GetChild( tree, ref.hItem )) != NULL )
369+ /*
370+ * ...which we also promote as a match.
371+ */
372+ return is_child_affiliate( tree, &ref, name );
373+
374+ /* If we get to here, the selected tree item doesn't match;
375+ * nor does it have any descendants among which a possible
376+ * match might be found.
377+ */
378+ return false;
379+}
380+
381+/* We use static methods to reference the tree view class object;
382+ * thus, we must define a static reference pointer. (Note that its
383+ * initial assignment to NULL will be updated, when the tree view
384+ * class object is instantiated).
385+ */
386+HWND AppWindowMaker::PackageTreeView = NULL;
387+bool AppWindowMaker::IsPackageGroupAffiliate( pkgXmlNode *package )
388+{
389+ /* Method to determine if a particular pkgXmlNode object is
390+ * an affiliate of the currently selected package group, as
391+ * specified in the tree view of the package group hierarchy.
392+ * Note that this must be a static method, so that it may be
393+ * invoked by methods of the pkgXmlNode object, without any
394+ * requirement for that object to hold a reference to the
395+ * AppWindowMaker object which owns the tree view.
396+ */
397+ static const char *group_key = "group";
398+ static const char *affiliate_key = "affiliate";
399+
400+ /* An affiliation may be declared at any level, between the
401+ * root of the XML document, and the pkgXmlNode specifications
402+ * to which it applies; save an end-point reference, so that
403+ * we may avoid overrunning the document root, as we walk
404+ * back through the document...
405+ */
406+ pkgXmlNode *top = package->GetDocumentRoot();
407+ while( package != top )
408+ {
409+ /* ...in search of applicable "affiliate" declarations.
410+ */
411+ pkgXmlNode *group = package->FindFirstAssociate( affiliate_key );
412+ while( group != NULL )
413+ {
414+ /* For each declared affiliation, check if it matches the
415+ * current selection in the package group hierarchy...
416+ */
417+ const char *group_name = group->GetPropVal( group_key, NULL );
418+ if( is_affiliated( PackageTreeView, group_name ) )
419+ /*
420+ * ...immediately returning any successful match; (just one
421+ * positive match is sufficient to determine affiliation).
422+ */
423+ return true;
424+
425+ /* When no affiliation has yet been identified, repeat the
426+ * check for any further "affiliate" declarations at the
427+ * current level within the XML document hierarchy...
428+ */
429+ group = group->FindNextAssociate( group_key );
430+ }
431+ /* ...and at enclosing levels, as may be necessary.
432+ */
433+ package = package->GetParent();
106434 }
435+ /* If we get to here, then we found no "affiliate" declarations to
436+ * be evaluated; only a root match in the package group hierarchy
437+ * is sufficient, to determine affiliation.
438+ */
439+ return is_affiliated( PackageTreeView, NULL );
107440 }
108441
109442 /* $RCSfile$: end of file */