OSDN Git Service

Don't attempt to resolve dependencies for unidentified packages.
[mingw/mingw-get.git] / src / pkgdata.cpp
index 0934200..841e07b 100644 (file)
@@ -4,7 +4,7 @@
  * $Id$
  *
  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
- * Copyright (C) 2012, MinGW Project
+ * Copyright (C) 2012, 2013, MinGW.org Project
  *
  *
  * Implementation of the classes and methods required to support the
@@ -38,6 +38,7 @@
 #include "pkgkeys.h"
 #include "pkginfo.h"
 #include "pkglist.h"
+#include "pkgproc.h"
 #include "pkgtask.h"
 
 #include <windowsx.h>
@@ -293,6 +294,19 @@ bool pkgTroffLayoutEngine::WriteLn( HDC canvas, RECT *bounds )
        */
       TextOutW( canvas, bounds->left, bounds->top - offset, linebuf, filled );
     }
+    else if( (fold == 0) && (new_width > max_width) )
+      /*
+       * The output line which we've just processed lies outside
+       * the viewport.  We note that it's initial (non-breakable)
+       * "word" would require more display width than the viewport
+       * can accommodate, if it were to be moved into the visible
+       * region; thus, this "word" will continue to be presented
+       * to the formatting engine, as the next input "word" to be
+       * processed.  This would result in an infinite loop, so we
+       * MUST discard this "word" from the input queue.
+       */
+      curr = (pkgTroffLayoutEngine *)(curr->next);
+
     /* Finally, adjust the top boundary of the viewport, to indicate
      * where the NEXT output line, if any, is to be positioned, and
      * return TRUE, to indicate that an output line was processed.
@@ -337,6 +351,7 @@ class DataSheetMaker: public ChildWindowMaker
     static int DisplayLicenceURL( const char * );
     static int DisplayPackageURL( const char * );
     inline void DisplayDescription( pkgXmlNode * );
+    inline void DisplayFilesManifest( pkgXmlNode * );
     void ComposeDescription( pkgXmlNode *, pkgXmlNode * );
     int FormatRecord( int, const char *, const char * );
     inline void FormatText( const char * );
@@ -569,6 +584,81 @@ void DataSheetMaker::ComposeDescription( pkgXmlNode *ref, pkgXmlNode *root )
   }
 }
 
+void DataSheetMaker::DisplayFilesManifest( pkgXmlNode *ref )
+{
+  /* Helper method to compile the list of files installed by a package,
+   * for display on the "Installed Files" tab of the data sheet panel.
+   */
+  pkgActionItem avail;
+  if( (ref = pkgGetStatus( ref, &avail )) == NULL )
+  {
+    /* This represents a package which is available, but has not been
+     * installed; we simply decline to compile the files list.
+     */
+    FormatRecord( 0, "Package",
+       avail.Selection()->GetPropVal( tarname_key, value_unknown )
+      );
+    bounding_box.top += PARAGRAPH_MARGIN;
+    FormatText(
+               "This package has not been installed; "
+       "the list of installed files is not available for packages "
+       "which have not been installed."
+      );
+  }
+  else
+  { /* This represents a package which has been installed; begin
+     * compilation of the list of installed files.
+     */
+    const char *tarname;
+    FormatRecord( 0, "Package",
+       tarname = ref->GetPropVal( tarname_key, value_unknown )
+      );
+    if( match_if_explicit( ref->ArchiveName(), value_none ) )
+    {
+      /* This is a meta-package; there are no files to list.
+       */
+      bounding_box.top += PARAGRAPH_MARGIN;
+      FormatText(
+         "This meta-package facilitates the installation of a collection "
+         "of other logically related packages; it provides no files, and "
+         "may be safely removed."
+       );
+    }
+    else
+    { /* This is a real package; retrieve the manifest of installed files,
+       * which was created during the installation process.
+       */
+      pkgXmlNode *index;
+      pkgManifest inventory( package_key, tarname );
+      if( (index = inventory.GetRoot()->FindFirstAssociate( manifest_key )) != NULL )
+      {
+       /* We've located a files list within the manifest; process it...
+        */
+       FormatRecord( 0, "This package provides the following files", "" );
+       bounding_box.left += LEFT_MARGIN; bounding_box.top += PARAGRAPH_MARGIN;
+       do { if( (ref = index->FindFirstAssociate( filename_key )) != NULL )
+              /*
+               * We found at least one file name within the list; emit it
+               * to the display, followed by any additional names present.
+               */
+              do { FormatText( ref->GetPropVal( pathname_key, value_unknown ) );
+                 } while( (ref = ref->FindNextAssociate( filename_key )) != NULL );
+
+            /* There should be no more than one, but check for, and process
+             * any additional files lists which may be present.
+             */
+          } while( (index = index->FindNextAssociate( manifest_key )) != NULL );
+      }
+      else
+      { /* The manifest appears to be lacking any files list; diagnose.
+        */
+       bounding_box.top += PARAGRAPH_MARGIN;
+       FormatText( "This package appears to provide no files." );
+      }
+    }
+  }
+}
+
 static pkgXmlNode *pkgListSelection( HWND package_ref, LVITEM *lookup )
 {
   /* Helper function, to retrieve the active selection from the
@@ -668,6 +758,14 @@ long DataSheetMaker::OnPaint()
        DisplayDescription( pkgListSelection( PackageRef, &lookup ) );
        break;
 
+      case PKG_DATASHEET_INSTALLED_FILES:
+       /* Available only for packages which have been installed,
+        * this comprises the files list content from the manifest
+        * which was created during the installation process.
+        */
+       DisplayFilesManifest( pkgListSelection( PackageRef, &lookup ) );
+       break;
+
       default:
        /* Handle requests for data sheets for which we have yet
         * to provide a compiling routine.
@@ -849,7 +947,7 @@ void AppWindowMaker::InitPackageTabControl()
    */
   TCITEM tab;
   tab.mask = TCIF_TEXT;
-  char *TabLegend[] =
+  const char *TabLegend[] =
   { "General", "Description", "Dependencies", "Installed Files", "Versions",
 
     /* ...with a NULL sentinel marking the preceding label as
@@ -861,7 +959,7 @@ void AppWindowMaker::InitPackageTabControl()
   {
     /* This loop assumes responsibility for actual tab creation...
      */
-    tab.pszText = TabLegend[i];
+    tab.pszText = (char *)(TabLegend[i]);
     if( TabCtrl_InsertItem( PackageTabControl, i, &tab ) == -1 )
     {
       /* ...bailing out, and deleting the container window,
@@ -1176,6 +1274,14 @@ void AppWindowMaker::SelectPackageAction( unsigned mode )
   }
 }
 
+void AppWindowMaker::UpdateDataSheet( void )
+{
+  /* Helper method, called when we wish to update the data sheet
+   * panel, to match the current list view and tab selection.
+   */
+  DataSheet->DisplayData( PackageTabControl, PackageListView );
+}
+
 long AppWindowMaker::OnNotify( WPARAM client_id, LPARAM data )
 {
   /* Handler for notifiable events to be processed in the context
@@ -1193,10 +1299,12 @@ long AppWindowMaker::OnNotify( WPARAM client_id, LPARAM data )
     case ID_PACKAGE_LISTVIEW:
       if( ((NMHDR *)(data))->code == NM_RCLICK )
       {
-       /* A right button mouse click within the package list view
-        * selects the package under the cursor, and offers a pop-up
-        * menu of actions which may be performed on it.
+       /* A right mouse button click within the package list view
+        * selects the package under the cursor, refreshing the tab
+        * pane to display its associated data sheet, and offers a
+        * pop-up menu of actions which may be performed on it.
         */
+       UpdateDataSheet();
        SelectPackageAction( LVHT_ONITEMICON | LVHT_ONITEMLABEL );
        break;
       }
@@ -1210,7 +1318,7 @@ long AppWindowMaker::OnNotify( WPARAM client_id, LPARAM data )
        /* ...each of which may require the data sheet content
         * to be updated, (to reflect a changed selection).
         */
-       DataSheet->DisplayData( PackageTabControl, PackageListView );
+       UpdateDataSheet();
 
        /* Additionally, for a left click on the package status
         * icon within the list view, we present a pop-up menu
@@ -1220,6 +1328,66 @@ long AppWindowMaker::OnNotify( WPARAM client_id, LPARAM data )
          SelectPackageAction( LVHT_ONITEMICON );
       }
       break;
+
+    /* We also need to consider notifications from the tree view...
+     */
+    case ID_PACKAGE_TREEVIEW:
+      if( ((NMHDR *)(data))->code == TVN_SELCHANGED )
+      {
+       /* ...from which we are interested only in notifications
+        * that the user has changed the package group selection.
+        *
+        * First, we ensure that any children of the selected
+        * package group are made visible.
+        */
+       TreeView_Expand( PackageTreeView,
+           ((NMTREEVIEW *)(data))->itemNew.hItem, TVE_EXPAND
+         );
+
+       /* We then clear out the previous content of the list view
+        * pane, and reconstruct it with new content, as determined
+        * by the new package group selection...
+        */
+       ClearPackageList();
+       UpdatePackageList();
+
+       /* ...and reapply any scheduled action markers, which may
+        * be applicable.
+        */
+       MarkSchedule( pkgData->Schedule() );
+
+       /* Finally, provided the previous selection is not an
+        * ancestor of the current, we may collapse any visible
+        * subtree descending from the previous.
+        *
+        * FIXME: We may wish to avoid collapsing any subtree
+        * which was designated as "expanded", in the original
+        * group hierarchy specification.  We may also wish to
+        * provide a user option, to disable this feature.
+        */
+       bool may_fold = true;
+       HTREEITEM prev = ((NMTREEVIEW *)(data))->itemNew.hItem;
+       while( may_fold && (prev != NULL) )
+       { if( prev == ((NMTREEVIEW *)(data))->itemOld.hItem )
+           /*
+            * Previous selection IS an ancestor of current;
+            * we must not collapse it.
+            */
+           may_fold = false;
+
+         else
+           /* Continue tracing ancestry, back to the root.
+            */
+           prev = TreeView_GetParent( PackageTreeView, prev );
+       }
+       if( may_fold )
+         /*
+          * Previous selection may be collapsed; do so.
+          */
+         TreeView_Expand( PackageTreeView,
+             ((NMTREEVIEW *)(data))->itemOld.hItem, TVE_COLLAPSE
+           );
+      }
   }
   /* Otherwise, this return causes any other notifiable events
    * to be simply ignored, (as they were by the original stub).
@@ -1255,9 +1423,11 @@ unsigned long pkgActionItem::EnumeratePendingActions( int classified )
       int action;
       if( (action = item->flags & ACTION_MASK) != 0 )
       {
-       /* ...and, when one is found...
+       /* ...and, when one is found, (noting that ACTION_UPGRADE may
+        * also be considered as a special case of ACTION_INSTALL)...
         */
-       if( action == classified )
+       if(  (action == classified)
+       ||  ((action == ACTION_UPGRADE) && (classified == ACTION_INSTALL))  )
        {
          /* ...and it matches the classification in which
           * we are interested, then we retrieve the tarname
@@ -1282,11 +1452,16 @@ unsigned long pkgActionItem::EnumeratePendingActions( int classified )
            ++count;
          }
        }
-       else if( classified == 0 )
+       else if( (classified == 0)
+         /*
+          * ...otherwise, when we aren't interested in any particular
+          * class of action regardless of classification...
+          */
+       || ((classified == ACTION_UNSUCCESSFUL) && ((flags & classified) != 0)) )
          /*
-          * ...otherwise, when we aren't interested in any
-          * particular class of action, just count all those
-          * which are found, regardless of classification.
+          * ...or when we are checking for unsuccessful actions, we
+          * count all those which are found, either unclassified, or
+          * marked as unsuccessful, respectively.
           */
          ++count;
       }
@@ -1308,15 +1483,7 @@ long AppWindowMaker::OnClose()
    * option for the termination request, so that the user
    * has an opportunity to complete such actions.
    */
-  if( (pkgData->Schedule()->EnumeratePendingActions() > 0)
-  &&  (MessageBox( AppWindow,
-       "You have marked changes which have not been applied;\n"
-       "these will be lost, if you quit without applying them.\n\n"
-       "Are you sure you want to discard these marked changes?",
-       "Discard Marked Changes?", MB_YESNO | MB_ICONWARNING
-      ) == IDNO)
-    ) return 0;
-  return -1;
+  return ConfirmActionRequest( "quit" ) ? -1 : 0;
 }
 
 /* $RCSfile$: end of file */