//+----------------------------------------------------------------------------+ //| Description: Magic Set Editor - Program to make Magic (tm) cards | //| Copyright: (C) 2001 - 2008 Twan van Laarhoven and "coppro" | //| License: GNU General Public License 2 or later (see file COPYING) | //+----------------------------------------------------------------------------+ // ----------------------------------------------------------------------------- : Includes #include #include #include #include #include #include #include DECLARE_TYPEOF_COLLECTION(InstallablePackageP); DECLARE_TYPEOF_COLLECTION(PackageUpdateList::TreeItemP); DECLARE_TYPEOF_COLLECTION(TreeList::ItemP); // ----------------------------------------------------------------------------- : PackageUpdateList::TreeItem void PackageUpdateList::TreeItem::add(const InstallablePackageP& package, const String& path, int level) { // this node this->level = level; PackageType new_type = package_type(*package->description); int new_hint = package->description->position_hint; if (new_type < position_type || (new_type == position_type && new_hint < position_hint)) { // this is a lower position hint, use it position_type = new_type; position_hint = new_hint; } // end of the path? if (path.empty()) { assert(!this->package); this->package = package; return; } // split path size_t pos = path.find_first_of(_('/')); String name = path.substr(0,pos); String rest = pos == String::npos ? _("") : path.substr(pos+1); // find/add child FOR_EACH(ti, children) { if (ti->label == name) { // already have this child if (pos == String::npos && ti->package) { // two packages with the same path TreeItemP ti2(new TreeItem); ti2->label = name; children.insert(ti_IT.first, ti2); ti2->add(package, rest, level + 1); } else { ti->add(package, rest, level + 1); } return; } } // don't have this child TreeItemP ti(new TreeItem); children.push_back(ti); ti->label = name; ti->add(package, rest, level + 1); } bool compare_pos_hint(const PackageUpdateList::TreeItemP& a, const PackageUpdateList::TreeItemP& b) { if (a->position_type < b->position_type) return true; if (a->position_type > b->position_type) return false; if (a->position_hint < b->position_hint) return true; if (a->position_hint > b->position_hint) return false; return a->label < b->label; } void PackageUpdateList::TreeItem::toItems(vector& items) { sort(children.begin(), children.end(), compare_pos_hint); FOR_EACH(c, children) { items.push_back(c); c->toItems(items); } } bool PackageUpdateList::TreeItem::highlight() const { if (package && package->willBeInstalled()) return true; FOR_EACH_CONST(c,children) if (c->highlight()) return true; return false; } PackageUpdateList::TreeItem::PackageType PackageUpdateList::TreeItem::package_type(const PackageDescription& desc) { if (desc.name == mse_package) return TYPE_PROG; size_t pos = desc.name.find_last_of(_('.')); if (pos == String::npos) return TYPE_OTHER; if (is_substr(desc.name,pos,_(".mse-locale"))) return TYPE_LOCALE; if (is_substr(desc.name,pos,_(".mse-game"))) return TYPE_GAME; if (is_substr(desc.name,pos,_(".mse-style"))) return TYPE_STYLESHEET; if (is_substr(desc.name,pos,_(".mse-export-template"))) return TYPE_EXPORT_TEMPLATE; if (is_substr(desc.name,pos,_(".mse-symbol-font"))) return TYPE_SYMBOL_FONT; if (is_substr(desc.name,pos,_(".mse-include"))) return TYPE_INCLUDE; if (is_substr(desc.name,pos,_(".ttf"))) return TYPE_FONT; return TYPE_OTHER; } void PackageUpdateList::TreeItem::setIcon(const Image& img) { Image image = img; int iw = image.GetWidth(), ih = image.GetHeight(); if (ih > 107) { int w = 107 * iw / ih; image = resample(image, w, 107); } else if (iw > 107) { int h = 107 * ih / iw; image = resample(image, 107, h); } if (package) package->description->icon = image; Image resampled = resample_preserve_aspect(image,16,16); icon = Bitmap(resampled); saturate(resampled, -.75); set_alpha(resampled,0.5); icon_grey = Bitmap(resampled); } // ----------------------------------------------------------------------------- : PackageIconRequest /// wx doesn't allow seeking on InputStreams from a wxURL /// The built in buffer class is too stupid to seek, so we must do it ourselfs class SeekAtStartInputStream : public wxFilterInputStream { public: SeekAtStartInputStream(wxInputStream& stream) : wxFilterInputStream(stream) , buffer_pos(0) { m_parent_i_stream->Read(buffer, 1024); buffer_size = m_parent_i_stream->LastRead(); } bool IsSeekable() const { return true; } protected: virtual size_t OnSysRead(void *buffer, size_t bufsize) { size_t len = min(buffer_size - buffer_pos, bufsize); memcpy(buffer, this->buffer + buffer_pos, len); buffer_pos += len; m_parent_i_stream->Read((Byte*)buffer + len, bufsize - len); return m_parent_i_stream->LastRead() + len; } virtual wxFileOffset OnSysSeek(wxFileOffset seek, wxSeekMode mode) { if (mode == wxFromStart) buffer_pos = seek; else if (mode == wxFromCurrent) buffer_pos += seek; else assert(false); assert(buffer_pos < buffer_size); return buffer_pos; } virtual wxFileOffset OnSysTell() const { assert(buffer_pos < buffer_size); return buffer_pos; } private: size_t buffer_size, buffer_pos; Byte buffer[1024]; }; /// Retrieve the icon for a package class PackageIconRequest : public ThumbnailRequest { public: PackageIconRequest(PackageUpdateList* list, PackageUpdateList::TreeItem* ti) : ThumbnailRequest( list, _("package_") + ti->package->description->icon_url + _("_") + ti->package->description->version.toString(), wxDateTime(1,wxDateTime::Jan,2000)) , list(list), ti(ti) {} virtual Image generate() { wxURL url(ti->package->description->icon_url); scoped_ptr isP(url.GetInputStream()); if (!isP) return wxImage(); SeekAtStartInputStream is2(*isP); Image result(is2); return result; } virtual void store(const Image& image) { if (!image.Ok()) return; ti->setIcon(image); list->Refresh(false); } private: PackageUpdateList* list; PackageUpdateList::TreeItem* ti; }; // ----------------------------------------------------------------------------- : PackageUpdateList : implementation PackageUpdateList::PackageUpdateList(Window* parent, const InstallablePackages& packages, bool show_only_installable, int id) : TreeList(parent, id) , show_only_installable(show_only_installable) , packages(packages) { item_height = max(item_height,19); rebuild(); } PackageUpdateList::~PackageUpdateList() { thumbnail_thread.abort(this); } void PackageUpdateList::initItems() { // add packages to tree TreeItem root; FOR_EACH_CONST(ip, packages) { String group = ip->description->installer_group; if (group.empty()) group = _("custom"); if (!show_only_installable || ip->installer) { root.add(ip, group); } } // tree to treelist items items.clear(); root.toItems(items); // init image list FOR_EACH(i,items) { TreeItem& ti = static_cast(*i); const InstallablePackageP& p = ti.package; // load icon Image image; if (p && p->description->icon.Ok()) { // it has an icon ti.setIcon(p->description->icon); } else if (p) { // it doesn't have an icon (yet) ti.setIcon(load_resource_image(_("installer_package"))); if (!p->description->icon_url.empty()) { // download icon thumbnail_thread.request(new_intrusive2(this,&ti)); } } else if (ti.position_type == TreeItem::TYPE_LOCALE) { // locale folder ti.setIcon(load_resource_image(_("installer_locales"))); } else { // other folder ti.setIcon(load_resource_image(_("installer_group"))); } } } void PackageUpdateList::drawItem(DC& dc, size_t index, size_t column, int x, int y, bool selected) const { // offset for drawing x += 1; y += 3; // the item const TreeItem& ti = static_cast(*items[index]); Color color = wxSystemSettings::GetColour(selected ? wxSYS_COLOUR_HIGHLIGHTTEXT : wxSYS_COLOUR_WINDOWTEXT); if (column == 0) { // Name const Bitmap& bmp = ti.highlight() ? ti.icon : ti.icon_grey; if (bmp.Ok()) dc.DrawBitmap(bmp, x-1,y-2); dc.SetTextForeground(color); dc.DrawText(capitalize_sentence(ti.label), x+17, y); } else if (column == 1 && ti.package) { // Status InstallablePackage& package = *ti.package; if (package.has(PACKAGE_CONFLICTS)) { dc.SetTextForeground(lerp(color,Color(255,0,0),0.8)); dc.DrawText(_LABEL_("package conflicts"), x,y); } else if (package.has(PACKAGE_MODIFIED)) { dc.SetTextForeground(lerp(color,Color(255,255,0),0.5)); dc.DrawText(_LABEL_("package modified"), x,y); } else if (package.has(PACKAGE_UPDATES)) { dc.SetTextForeground(lerp(color,Color(0,0,255),0.5)); dc.DrawText(_LABEL_("package updates"), x,y); } else if (package.has(PACKAGE_INSTALLED)) { dc.SetTextForeground(color); dc.DrawText(_LABEL_("package installed"), x,y); } else if (package.has(PACKAGE_INSTALLABLE)) { dc.SetTextForeground(lerp(color,Color(128,128,128),0.6)); dc.SetTextForeground(color); dc.DrawText(_LABEL_("package installable"), x,y); } } else if (column == 2 && ti.package) { // Action InstallablePackage& package = *ti.package; if (package.has(PACKAGE_ACT_INSTALL)) { if (package.has(PACKAGE_UPDATES)) { dc.SetTextForeground(lerp(color,Color(0,0,255),0.6)); dc.DrawText(_LABEL_("upgrade package"), x,y); } else if (package.has(PACKAGE_INSTALLED)) { dc.SetTextForeground(lerp(color,Color(0,0,255),0.2)); dc.DrawText(_LABEL_("reinstall package"), x,y); } else { dc.SetTextForeground(lerp(color,Color(0,255,0),0.6)); dc.DrawText(_LABEL_("install package"), x,y); } } else if (package.has(PACKAGE_ACT_REMOVE)) { dc.SetTextForeground(lerp(color,Color(255,0,0),0.7)); dc.DrawText(_LABEL_("remove package"), x,y); } } } String PackageUpdateList::columnText(size_t column) const { if (column == 0) return _LABEL_("package name"); else if (column == 1) return _LABEL_("package status"); else if (column == 2) return _LABEL_("package action"); else throw InternalError(_("Unknown column")); } int PackageUpdateList::columnWidth(size_t column) const { if (column == 0) { wxSize cs = GetClientSize(); return cs.x - 240; } else { return 120; } }