Files
MagicSetEditor2/src/gui/package_update_list.cpp
T
twanvl 20b40585f6 Some changes to the package manager:
- use a single button for install/upgrade
 - show versions of installed package and installer
Named actions in enum PACKAGE_ACT_* to avoid confusion with the status enum.

git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@1186 0fc631ac-6414-0410-93d0-97cfa31319b6
2008-08-31 18:57:55 +00:00

309 lines
11 KiB
C++

//+----------------------------------------------------------------------------+
//| 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 <util/prec.hpp>
#include <gui/package_update_list.hpp>
#include <gui/thumbnail_thread.hpp>
#include <gui/util.hpp>
#include <gfx/gfx.hpp>
#include <boost/scoped_ptr.hpp>
#include <wx/url.h>
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<TreeList::ItemP>& 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<wxInputStream> 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<TreeItem&>(*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<PackageIconRequest>(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<const TreeItem&>(*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;
}
}