/** * Reverse lookup the dependencies of all parents over a given child. * @param tree list to store all parents in (is not cleared) * @param child the child to search the parents' dependencies for */ void ClientNetworkContentSocketHandler::ReverseLookupTreeDependency(ConstContentVector &tree, const ContentInfo *child) const { *tree.Append() = child; /* First find all direct parents */ for (ConstContentIterator iter = tree.Begin(); iter != tree.End(); iter++) { ConstContentVector parents; this->ReverseLookupDependency(parents, *iter); for (ConstContentIterator piter = parents.Begin(); piter != parents.End(); piter++) { tree.Include(*piter); } } }
/** * Reverse lookup the dependencies of all parents over a given child. * @param tree list to store all parents in (is not cleared) * @param child the child to search the parents' dependencies for */ void ClientNetworkContentSocketHandler::ReverseLookupTreeDependency(ConstContentVector &tree, const ContentInfo *child) const { *tree.Append() = child; /* First find all direct parents. We can't use the "normal" iterator as * we are including stuff into the vector and as such the vector's data * store can be reallocated (and thus move), which means out iterating * pointer gets invalid. So fall back to the indices. */ for (uint i = 0; i < tree.Length(); i++) { ConstContentVector parents; this->ReverseLookupDependency(parents, tree[i]); for (ConstContentIterator piter = parents.Begin(); piter != parents.End(); piter++) { tree.Include(*piter); } } }
/** * Check the dependencies (recursively) of this content info * @param ci the content info to check the dependencies of */ void ClientNetworkContentSocketHandler::CheckDependencyState(ContentInfo *ci) { if (ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) { /* Selection is easy; just walk all children and set the * autoselected state. That way we can see what we automatically * selected and thus can unselect when a dependency is removed. */ for (uint i = 0; i < ci->dependency_count; i++) { ContentInfo *c = this->GetContent(ci->dependencies[i]); if (c == NULL) { this->DownloadContentInfo(ci->dependencies[i]); } else if (c->state == ContentInfo::UNSELECTED) { c->state = ContentInfo::AUTOSELECTED; this->CheckDependencyState(c); } } return; } if (ci->state != ContentInfo::UNSELECTED) return; /* For unselection we need to find the parents of us. We need to * unselect them. After that we unselect all children that we * depend on and are not used as dependency for us, but only when * we automatically selected them. */ ConstContentVector parents; this->ReverseLookupDependency(parents, ci); for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) { const ContentInfo *c = *iter; if (!c->IsSelected()) continue; this->Unselect(c->id); } for (uint i = 0; i < ci->dependency_count; i++) { const ContentInfo *c = this->GetContent(ci->dependencies[i]); if (c == NULL) { DownloadContentInfo(ci->dependencies[i]); continue; } if (c->state != ContentInfo::AUTOSELECTED) continue; /* Only unselect when WE are the only parent. */ parents.Clear(); this->ReverseLookupDependency(parents, c); /* First check whether anything depends on us */ int sel_count = 0; bool force_selection = false; for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) { if ((*iter)->IsSelected()) sel_count++; if ((*iter)->state == ContentInfo::SELECTED) force_selection = true; } if (sel_count == 0) { /* Nothing depends on us */ this->Unselect(c->id); continue; } /* Something manually selected depends directly on us */ if (force_selection) continue; /* "Flood" search to find all items in the dependency graph*/ parents.Clear(); this->ReverseLookupTreeDependency(parents, c); /* Is there anything that is "force" selected?, if so... we're done. */ for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) { if ((*iter)->state != ContentInfo::SELECTED) continue; force_selection = true; break; } /* So something depended directly on us */ if (force_selection) continue; /* Nothing depends on us, mark the whole graph as unselected. * After that's done run over them once again to test their children * to unselect. Don't do it immediatelly because it'll do exactly what * we're doing now. */ for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) { const ContentInfo *c = *iter; if (c->state == ContentInfo::AUTOSELECTED) this->Unselect(c->id); } for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) { this->CheckDependencyState(this->GetContent((*iter)->id)); } } }
/** * Helper function to draw the details part of this window. * @param r the rectangle to stay within while drawing */ void DrawDetails(const Rect &r) const { static const int DETAIL_LEFT = 5; ///< Number of pixels at the left static const int DETAIL_RIGHT = 5; ///< Number of pixels at the right static const int DETAIL_TOP = 5; ///< Number of pixels at the top /* Height for the title banner */ int DETAIL_TITLE_HEIGHT = 5 * FONT_HEIGHT_NORMAL; /* Create the nice grayish rectangle at the details top */ GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.top + DETAIL_TITLE_HEIGHT, PC_DARK_BLUE); DrawString(r.left + WD_INSET_LEFT, r.right - WD_INSET_RIGHT, r.top + FONT_HEIGHT_NORMAL + WD_INSET_TOP, STR_CONTENT_DETAIL_TITLE, TC_FROMSTRING, SA_HOR_CENTER); /* Draw the total download size */ SetDParam(0, this->filesize_sum); DrawString(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, r.bottom - FONT_HEIGHT_NORMAL - WD_PAR_VSEP_NORMAL, STR_CONTENT_TOTAL_DOWNLOAD_SIZE); if (this->selected == NULL) return; /* And fill the rest of the details when there's information to place there */ DrawStringMultiLine(r.left + WD_INSET_LEFT, r.right - WD_INSET_RIGHT, r.top + DETAIL_TITLE_HEIGHT / 2, r.top + DETAIL_TITLE_HEIGHT, STR_CONTENT_DETAIL_SUBTITLE_UNSELECTED + this->selected->state, TC_FROMSTRING, SA_CENTER); /* Also show the total download size, so keep some space from the bottom */ const uint max_y = r.bottom - FONT_HEIGHT_NORMAL - WD_PAR_VSEP_WIDE; int y = r.top + DETAIL_TITLE_HEIGHT + DETAIL_TOP; if (this->selected->upgrade) { SetDParam(0, STR_CONTENT_TYPE_BASE_GRAPHICS + this->selected->type - CONTENT_TYPE_BASE_GRAPHICS); y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_UPDATE); y += WD_PAR_VSEP_WIDE; } SetDParamStr(0, this->selected->name); y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_NAME); if (!StrEmpty(this->selected->version)) { SetDParamStr(0, this->selected->version); y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_VERSION); } if (!StrEmpty(this->selected->description)) { SetDParamStr(0, this->selected->description); y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_DESCRIPTION); } if (!StrEmpty(this->selected->url)) { SetDParamStr(0, this->selected->url); y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_URL); } SetDParam(0, STR_CONTENT_TYPE_BASE_GRAPHICS + this->selected->type - CONTENT_TYPE_BASE_GRAPHICS); y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_TYPE); y += WD_PAR_VSEP_WIDE; SetDParam(0, this->selected->filesize); y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_FILESIZE); if (this->selected->dependency_count != 0) { /* List dependencies */ char buf[DRAW_STRING_BUFFER] = ""; char *p = buf; for (uint i = 0; i < this->selected->dependency_count; i++) { ContentID cid = this->selected->dependencies[i]; /* Try to find the dependency */ ConstContentIterator iter = _network_content_client.Begin(); for (; iter != _network_content_client.End(); iter++) { const ContentInfo *ci = *iter; if (ci->id != cid) continue; p += seprintf(p, lastof(buf), p == buf ? "%s" : ", %s", (*iter)->name); break; } } SetDParamStr(0, buf); y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_DEPENDENCIES); } if (this->selected->tag_count != 0) { /* List all tags */ char buf[DRAW_STRING_BUFFER] = ""; char *p = buf; for (uint i = 0; i < this->selected->tag_count; i++) { p += seprintf(p, lastof(buf), i == 0 ? "%s" : ", %s", this->selected->tags[i]); } SetDParamStr(0, buf); y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_TAGS); } if (this->selected->IsSelected()) { /* When selected show all manually selected content that depends on this */ ConstContentVector tree; _network_content_client.ReverseLookupTreeDependency(tree, this->selected); char buf[DRAW_STRING_BUFFER] = ""; char *p = buf; for (ConstContentIterator iter = tree.Begin(); iter != tree.End(); iter++) { const ContentInfo *ci = *iter; if (ci == this->selected || ci->state != ContentInfo::SELECTED) continue; p += seprintf(p, lastof(buf), buf == p ? "%s" : ", %s", ci->name); } if (p != buf) { SetDParamStr(0, buf); y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_SELECTED_BECAUSE_OF); } } }