00001
00002
00003 #include <wibble/string.h>
00004
00005 #include <ept/token.h>
00006 #include <ept/core/desktopfile.h>
00007 #include <ept/core/source.h>
00008
00009 #include <set>
00010 #include <vector>
00011 #include <fstream>
00012 #include <sstream>
00013 #include <iterator>
00014 #include <functional>
00015
00016 #include <dirent.h>
00017
00018 #ifndef EPT_CORE_DESKTOP_H
00019 #define EPT_CORE_DESKTOP_H
00020
00021 namespace ept {
00022 namespace core {
00023 namespace desktop {
00024
00025 typedef enum { Name, Group, ShortDescription, Package, Icon } PropertyId;
00026
00027 template< PropertyId > struct PropertyType {};
00028 template<> struct PropertyType< Name > { typedef std::string T; };
00029 template<> struct PropertyType< Group > { typedef std::string T; };
00030 template<> struct PropertyType< ShortDescription > { typedef std::string T; };
00031 template<> struct PropertyType< Package > { typedef ept::Token T; };
00032 template<> struct PropertyType< Icon > { typedef std::string T; };
00033
00034 typedef std::set< std::string > Categories;
00035
00036 struct Category {
00037 std::string name;
00038 operator std::string() const { return name; }
00039 };
00040
00041 inline std::istream &operator >>( std::istream &i, Category &cat ) {
00042 char c;
00043 cat.name = "";
00044 while ( i.peek() != EOF ) {
00045 c = i.get();
00046 if ( c == ';' ) return i;
00047 cat.name += c;
00048 }
00049 return i;
00050 }
00051
00052 struct Entry : wibble::mixin::Comparable< Entry > {
00053 Entry() {}
00054 Entry( std::string n, std::string g,
00055 std::string p, std::string d , std::string i )
00056 : m_name( n ),
00057 m_package( p ),
00058 m_description( d ),
00059 m_icon( i )
00060 { setCategories( g ); }
00061
00062 void load( std::string file ) {
00063 m_id = file;
00064 std::ifstream i( file.c_str() );
00065 if ( !i.is_open() )
00066 return;
00067 desktop::File e;
00068 i >> e;
00069 i.close();
00070 desktop::File::Group &g = e.group( "Desktop Entry" );
00071 m_name = g.entry( "Name" ).value;
00072 m_description = g.entry( "Comment" ).value;
00073 if ( m_description == "" )
00074 m_description = g.entry( "GenericName" ).value;
00075 m_package = g.entry( "X-AppInstall-Package" ).value;
00076
00077 m_icon = g.entry( "Icon" ).value;
00078 setCategories( g.entry( "Categories" ).value );
00079 }
00080
00081 void setCategories( std::string s ) {
00082 std::istringstream i( s );
00083 m_categories.clear();
00084 std::remove_copy_if(
00085 std::istream_iterator< Category >( i ),
00086 std::istream_iterator< Category >(),
00087 std::inserter( m_categories, m_categories.begin() ),
00088 std::bind1st( std::equal_to< std::string >(), "" ) );
00089 }
00090
00091 Categories categories() const { return m_categories; }
00092 bool inCategory( std::string c ) const {
00093 return m_categories.find( c ) != m_categories.end();
00094 }
00095 std::string id() const { return m_id; }
00096 std::string name() const { return m_name; }
00097 std::string package() const { return m_package; }
00098 std::string description() const { return m_description; }
00099 std::string icon() const { return m_icon; }
00100 bool operator< ( const Entry &o ) const {
00101 if ( m_name < o.m_name ) return true;
00102 if ( m_name == o.m_name )
00103 if ( m_package < o.m_package ) return true;
00104 return false;
00105 }
00106 protected:
00107 std::string m_name, m_package, m_description, m_icon, m_id;
00108 bool m_supported, m_free;
00109 Categories m_categories;
00110 };
00111
00112 struct InternalList {
00113 std::string dir;
00114 std::string current;
00115 mutable Entry entry;
00116 off_t offset;
00117 mutable bool loaded;
00118
00119 InternalList() : dir( "" ), offset( -2 ), loaded( false ) {}
00120 InternalList( std::string d ) : dir( d ), offset( -1 ), loaded( false )
00121 {
00122 firstFile();
00123 }
00124
00125 Entry head() const {
00126 if (!loaded)
00127 entry.load( current );
00128 loaded = true;
00129 return entry;
00130 }
00131
00132 bool empty() const {
00133 return (offset == -2);
00134 }
00135
00136 void firstFile() {
00137 offset = -1;
00138 nextFile();
00139 }
00140
00141 InternalList tail() const {
00142 InternalList r = *this;
00143 r.nextFile();
00144 return r;
00145 }
00146
00147 void nextFile() {
00148 loaded = false;
00149 DIR *d = opendir( dir.c_str() );
00150 if ( !d ) {
00151 offset = -2;
00152 closedir( d );
00153 return;
00154 }
00155
00156 if ( offset != -1 )
00157 seekdir( d, offset );
00158
00159 dirent *ent = 0;
00160 while ( ( ent = readdir( d ) ) != 0 ) {
00161 std::string name( ent->d_name );
00162 if ( name == "." || name == ".." )
00163 continue;
00164 if ( !wibble::str::endsWith( name, ".desktop" ) )
00165 continue;
00166 current = dir + "/" + name;
00167 offset = telldir( d );
00168 closedir( d );
00169 return;
00170 }
00171 closedir( d );
00172 offset = -2;
00173 }
00174 };
00175
00176 struct Setup {
00177 typedef ept::Token Token;
00178 typedef Entry Internal;
00179 typedef desktop::PropertyId PropertyId;
00180 typedef desktop::InternalList InternalList;
00181 };
00182
00183 struct GroupPolicy {
00184 virtual std::string group( const Entry &e )
00185 {
00186 return wibble::str::fmt( e.categories() );
00187 }
00188 virtual ~GroupPolicy() {}
00189 };
00190
00191 struct Source : core::Source< Source, Setup, PropertyType >
00192 {
00193 std::string m_dir;
00194
00195 GroupPolicy m_defaultPolicy;
00196 GroupPolicy *m_policy;
00197
00198 Source( std::string dir ) : m_dir( dir ),
00199 m_policy( &m_defaultPolicy ) {}
00200
00201 InternalList listInternal() {
00202 return InternalList( m_dir );
00203 }
00204
00205 Token getToken( Entry i ) {
00206 Token t;
00207 t._id = std::string( "desktop:" ) + i.id();
00208 return t;
00209 }
00210
00211 Entry lookupToken( Token t ) {
00212 Entry e;
00213 e.load( t.desktop() );
00214 return e;
00215 }
00216
00217 void setGroupPolicy( GroupPolicy *p ) {
00218 m_policy = p;
00219 }
00220
00221 template< PropertyId p >
00222 typename PropertyType< p >::T getInternal( Entry );
00223
00224 struct IsInGroup {
00225 std::string g;
00226 IsInGroup( std::string _g = "" ) : g( _g ) {}
00227 bool operator()( Token, std::string gr ) const {
00228 return gr == g;
00229 }
00230 };
00231
00232 PropertyFilter< Group, IsInGroup >::T group( std::string id )
00233 {
00234 return propertyFilter< Group >( IsInGroup( id ) );
00235 }
00236
00237 static std::string projectGroup( ComposedList< Name > t ) {
00238 return t.get< Group >();
00239 }
00240
00241 list::Unique< list::Sorted<
00242 list::Map< ComposedList< Name >,
00243 __typeof( std::ptr_fun( &projectGroup ) ) > > >
00244 groupList() {
00245 return list::unique(
00246 list::sort( list::map( list< Name >(),
00247 std::ptr_fun( &projectGroup ) ) ) );
00248 }
00249 };
00250
00251 template<> inline std::string Source::getInternal< Name >( Entry e ) {
00252 return e.name();
00253 }
00254
00255 template<> inline std::string Source::getInternal< Icon >( Entry e ) {
00256 return e.icon();
00257 }
00258
00259 template<> inline ept::Token Source::getInternal< Package >( Entry e ) {
00260 ept::Token t;
00261 t._id = e.package();
00262 return t;
00263 }
00264
00265 template<> inline std::string Source::getInternal< Group >( Entry e ) {
00266 return m_policy->group( e );
00267 }
00268
00269 template<> inline std::string Source::getInternal< ShortDescription >( Entry e ) {
00270 return e.description();
00271 }
00272
00273 }
00274 }
00275 }
00276
00277 #endif