Imported Upstream version 0.25
[platform/upstream/desktop-file-utils.git] / src / validate.c
index c6011b0..62406ab 100644 (file)
@@ -112,6 +112,7 @@ struct _kf_validator {
 
   gboolean     kde_reserved_warnings;
   gboolean     no_deprecated_warnings;
+  gboolean     no_hints;
 
   char        *main_group;
   DesktopType  type;
@@ -127,6 +128,8 @@ struct _kf_validator {
   GHashTable  *action_groups;
 
   gboolean     fatal_error;
+
+  gboolean     use_colors;
 };
 
 static gboolean
@@ -210,6 +213,10 @@ handle_actions_key (kf_validator *kf,
                     const char   *locale_key,
                     const char   *value);
 static gboolean
+handle_dbus_activatable_key (kf_validator *kf,
+                             const char   *locale_key,
+                             const char   *value);
+static gboolean
 handle_dev_key (kf_validator *kf,
                 const char   *locale_key,
                 const char   *value);
@@ -311,6 +318,13 @@ static DesktopKeyDefinition registered_desktop_keys[] = {
   /* since 1.1 (used to be in the spec before 1.0, but was not really
    * specified) */
   { DESKTOP_STRING_LIST_TYPE,       "Actions",           FALSE, FALSE, FALSE, handle_actions_key },
+  /* Since 1.2 */
+  { DESKTOP_STRING_LIST_TYPE,       "Implements",        FALSE, FALSE, FALSE, NULL },
+
+  { DESKTOP_BOOLEAN_TYPE,           "DBusActivatable",   FALSE, FALSE, FALSE, handle_dbus_activatable_key },
+
+  /* Since 1.4 */
+  { DESKTOP_BOOLEAN_TYPE,           "PrefersNonDefaultGPU", FALSE, FALSE, FALSE, NULL },
 
   /* Keys reserved for KDE */
 
@@ -344,6 +358,8 @@ static DesktopKeyDefinition registered_desktop_keys[] = {
   /* since 0.9.6 */
   { DESKTOP_STRING_LIST_TYPE,       "SortOrder",         FALSE, TRUE,  FALSE, NULL },
   { DESKTOP_REGEXP_LIST_TYPE,       "FilePattern",       FALSE, TRUE,  FALSE, NULL },
+  /* since 1.4 */
+  { DESKTOP_BOOLEAN_TYPE,           "X-KDE-RunOnDiscreteGpu", FALSE, TRUE, FALSE, NULL },
 
   /* Keys from other specifications */
 
@@ -354,13 +370,24 @@ static DesktopKeyDefinition registered_desktop_keys[] = {
 static DesktopKeyDefinition registered_action_keys[] = {
   { DESKTOP_LOCALESTRING_TYPE,      "Name",               TRUE,  FALSE, FALSE, NULL },
   { DESKTOP_LOCALESTRING_TYPE,      "Icon",               FALSE, FALSE, FALSE, handle_icon_key },
-  { DESKTOP_STRING_LIST_TYPE,       "OnlyShowIn",         FALSE, FALSE, FALSE, handle_show_in_key },
-  { DESKTOP_STRING_LIST_TYPE,       "NotShowIn",          FALSE, FALSE, FALSE, handle_show_in_key },
+  { DESKTOP_STRING_LIST_TYPE,       "OnlyShowIn",         FALSE, TRUE, FALSE, handle_show_in_key },
+  { DESKTOP_STRING_LIST_TYPE,       "NotShowIn",          FALSE, TRUE, FALSE, handle_show_in_key },
   { DESKTOP_STRING_TYPE,            "Exec",               TRUE,  FALSE, FALSE, handle_exec_key }
 };
 
+/* This should be the same list as in xdg-specs/menu/menu-spec.xml */
 static const char *show_in_registered[] = {
-  "GNOME", "KDE", "LXDE", "MATE", "Razor", "ROX", "Unity", "XFCE", "Old"
+    "GNOME", "GNOME-Classic", "GNOME-Flashback", "KDE", "LXDE", "LXQt", "MATE", "Razor", "ROX", "TDE", "Unity", "XFCE", "EDE", "Cinnamon", "Pantheon", "Budgie", "Enlightenment", "Deepin", "Old"
+};
+
+static struct {
+  const char   *name;
+  const char   *first_arg[3];
+  unsigned int  additional_args;
+} registered_autostart_condition[] = {
+  { "GNOME",     { NULL }, 1 },
+  { "GNOME3",    { "if-session", "unless-session", NULL }, 1},
+  { "GSettings", { NULL }, 2 }
 };
 
 static struct {
@@ -368,150 +395,172 @@ static struct {
   gboolean    main;
   gboolean    require_only_show_in;
   gboolean    deprecated;
-  const char *requires[4];
+  const char *requires[2];
+  const char *suggests[4];
 } registered_categories[] = {
-  { "AudioVideo",             TRUE,  FALSE, FALSE, { NULL } },
-  { "Audio",                  TRUE,  FALSE, FALSE, { "AudioVideo", NULL } },
-  { "Video",                  TRUE,  FALSE, FALSE, { "AudioVideo", NULL } },
-  { "Development",            TRUE,  FALSE, FALSE, { NULL } },
-  { "Education",              TRUE,  FALSE, FALSE, { NULL } },
-  { "Game",                   TRUE,  FALSE, FALSE, { NULL } },
-  { "Graphics",               TRUE,  FALSE, FALSE, { NULL } },
-  { "Network",                TRUE,  FALSE, FALSE, { NULL } },
-  { "Office",                 TRUE,  FALSE, FALSE, { NULL } },
-  { "Settings",               TRUE,  FALSE, FALSE, { NULL } },
-  { "System",                 TRUE,  FALSE, FALSE, { NULL } },
-  { "Utility",                TRUE,  FALSE, FALSE, { NULL } },
-  { "Audio",                  FALSE, FALSE, FALSE, { "Development", NULL } },
-  { "Video",                  FALSE, FALSE, FALSE, { "Development", NULL } },
-  { "Building",               FALSE, FALSE, FALSE, { "Development", NULL } },
-  { "Debugger",               FALSE, FALSE, FALSE, { "Development", NULL } },
-  { "IDE",                    FALSE, FALSE, FALSE, { "Development", NULL } },
-  { "GUIDesigner",            FALSE, FALSE, FALSE, { "Development", NULL } },
-  { "Profiling",              FALSE, FALSE, FALSE, { "Development", NULL } },
-  { "RevisionControl",        FALSE, FALSE, FALSE, { "Development", NULL } },
-  { "Translation",            FALSE, FALSE, FALSE, { "Development", NULL } },
-  { "Calendar",               FALSE, FALSE, FALSE, { "Office", NULL } },
-  { "ContactManagement",      FALSE, FALSE, FALSE, { "Office", NULL } },
-  { "Database",               FALSE, FALSE, FALSE, { "Office", "Development", "AudioVideo", NULL } },
-  { "Dictionary",             FALSE, FALSE, FALSE, { "Office;TextTools", NULL } },
-  { "Chart",                  FALSE, FALSE, FALSE, { "Office", NULL } },
-  { "Email",                  FALSE, FALSE, FALSE, { "Office;Network", NULL } },
-  { "Finance",                FALSE, FALSE, FALSE, { "Office", NULL } },
-  { "FlowChart",              FALSE, FALSE, FALSE, { "Office", NULL } },
-  { "PDA",                    FALSE, FALSE, FALSE, { "Office", NULL } },
-  { "ProjectManagement",      FALSE, FALSE, FALSE, { "Office;Development", NULL } },
-  { "Presentation",           FALSE, FALSE, FALSE, { "Office", NULL } },
-  { "Spreadsheet",            FALSE, FALSE, FALSE, { "Office", NULL } },
-  { "WordProcessor",          FALSE, FALSE, FALSE, { "Office", NULL } },
-  { "2DGraphics",             FALSE, FALSE, FALSE, { "Graphics", NULL } },
-  { "VectorGraphics",         FALSE, FALSE, FALSE, { "Graphics;2DGraphics", NULL } },
-  { "RasterGraphics",         FALSE, FALSE, FALSE, { "Graphics;2DGraphics", NULL } },
-  { "3DGraphics",             FALSE, FALSE, FALSE, { "Graphics", NULL } },
-  { "Scanning",               FALSE, FALSE, FALSE, { "Graphics", NULL } },
-  { "OCR",                    FALSE, FALSE, FALSE, { "Graphics;Scanning", NULL } },
-  { "Photography",            FALSE, FALSE, FALSE, { "Graphics", "Office", NULL } },
-  { "Publishing",             FALSE, FALSE, FALSE, { "Graphics", "Office", NULL } },
-  { "Viewer",                 FALSE, FALSE, FALSE, { "Graphics", "Office", NULL } },
-  { "TextTools",              FALSE, FALSE, FALSE, { "Utility", NULL } },
-  { "DesktopSettings",        FALSE, FALSE, FALSE, { "Settings", NULL } },
-  { "HardwareSettings",       FALSE, FALSE, FALSE, { "Settings", NULL } },
-  { "Printing",               FALSE, FALSE, FALSE, { "HardwareSettings;Settings", NULL } },
-  { "PackageManager",         FALSE, FALSE, FALSE, { "Settings", NULL } },
-  { "Dialup",                 FALSE, FALSE, FALSE, { "Network", NULL } },
-  { "InstantMessaging",       FALSE, FALSE, FALSE, { "Network", NULL } },
-  { "Chat",                   FALSE, FALSE, FALSE, { "Network", NULL } },
-  { "IRCClient",              FALSE, FALSE, FALSE, { "Network", NULL } },
-  { "FileTransfer",           FALSE, FALSE, FALSE, { "Network", NULL } },
-  { "HamRadio",               FALSE, FALSE, FALSE, { "Network", "Audio", NULL } },
-  { "News",                   FALSE, FALSE, FALSE, { "Network", NULL } },
-  { "P2P",                    FALSE, FALSE, FALSE, { "Network", NULL } },
-  { "RemoteAccess",           FALSE, FALSE, FALSE, { "Network", NULL } },
-  { "Telephony",              FALSE, FALSE, FALSE, { "Network", NULL } },
-  { "TelephonyTools",         FALSE, FALSE, FALSE, { "Utility", NULL } },
-  { "VideoConference",        FALSE, FALSE, FALSE, { "Network", NULL } },
-  { "WebBrowser",             FALSE, FALSE, FALSE, { "Network", NULL } },
-  { "WebDevelopment",         FALSE, FALSE, FALSE, { "Network", "Development", NULL } },
-  { "Midi",                   FALSE, FALSE, FALSE, { "AudioVideo;Audio", NULL } },
-  { "Mixer",                  FALSE, FALSE, FALSE, { "AudioVideo;Audio", NULL } },
-  { "Sequencer",              FALSE, FALSE, FALSE, { "AudioVideo;Audio", NULL } },
-  { "Tuner",                  FALSE, FALSE, FALSE, { "AudioVideo;Audio", NULL } },
-  { "TV",                     FALSE, FALSE, FALSE, { "AudioVideo;Video", NULL } },
-  { "AudioVideoEditing",      FALSE, FALSE, FALSE, { "Audio", "Video", "AudioVideo", NULL } },
-  { "Player",                 FALSE, FALSE, FALSE, { "Audio", "Video", "AudioVideo", NULL } },
-  { "Recorder",               FALSE, FALSE, FALSE, { "Audio", "Video", "AudioVideo", NULL } },
-  { "DiscBurning",            FALSE, FALSE, FALSE, { "Audio", "Video", "AudioVideo", NULL } },
-  { "ActionGame",             FALSE, FALSE, FALSE, { "Game", NULL } },
-  { "AdventureGame",          FALSE, FALSE, FALSE, { "Game", NULL } },
-  { "ArcadeGame",             FALSE, FALSE, FALSE, { "Game", NULL } },
-  { "BoardGame",              FALSE, FALSE, FALSE, { "Game", NULL } },
-  { "BlocksGame",             FALSE, FALSE, FALSE, { "Game", NULL } },
-  { "CardGame",               FALSE, FALSE, FALSE, { "Game", NULL } },
-  { "KidsGame",               FALSE, FALSE, FALSE, { "Game", NULL } },
-  { "LogicGame",              FALSE, FALSE, FALSE, { "Game", NULL } },
-  { "RolePlaying",            FALSE, FALSE, FALSE, { "Game", NULL } },
-  { "Simulation",             FALSE, FALSE, FALSE, { "Game", NULL } },
-  { "SportsGame",             FALSE, FALSE, FALSE, { "Game", NULL } },
-  { "StrategyGame",           FALSE, FALSE, FALSE, { "Game", NULL } },
-  { "Art",                    FALSE, FALSE, FALSE, { "Education", NULL } },
-  { "Construction",           FALSE, FALSE, FALSE, { "Education", NULL } },
-  { "Music",                  FALSE, FALSE, FALSE, { "AudioVideo;Education", NULL } },
-  { "Languages",              FALSE, FALSE, FALSE, { "Education", NULL } },
-  { "Science",                FALSE, FALSE, FALSE, { "Education", NULL } },
-  { "ArtificialIntelligence", FALSE, FALSE, FALSE, { "Education;Science", NULL } },
-  { "Astronomy",              FALSE, FALSE, FALSE, { "Education;Science", NULL } },
-  { "Biology",                FALSE, FALSE, FALSE, { "Education;Science", NULL } },
-  { "Chemistry",              FALSE, FALSE, FALSE, { "Education;Science", NULL } },
-  { "ComputerScience",        FALSE, FALSE, FALSE, { "Education;Science", NULL } },
-  { "DataVisualization",      FALSE, FALSE, FALSE, { "Education;Science", NULL } },
-  { "Economy",                FALSE, FALSE, FALSE, { "Education", NULL } },
-  { "Electricity",            FALSE, FALSE, FALSE, { "Education;Science", NULL } },
-  { "Geography",              FALSE, FALSE, FALSE, { "Education", NULL } },
-  { "Geology",                FALSE, FALSE, FALSE, { "Education;Science", NULL } },
-  { "Geoscience",             FALSE, FALSE, FALSE, { "Education;Science", NULL } },
-  { "History",                FALSE, FALSE, FALSE, { "Education", NULL } },
-  { "ImageProcessing",        FALSE, FALSE, FALSE, { "Education;Science", NULL } },
-  { "Literature",             FALSE, FALSE, FALSE, { "Education", NULL } },
-  { "Math",                   FALSE, FALSE, FALSE, { "Education;Science", NULL } },
-  { "NumericalAnalysis",      FALSE, FALSE, FALSE, { "Education;Science;Math", NULL } },
-  { "MedicalSoftware",        FALSE, FALSE, FALSE, { "Education;Science", NULL } },
-  { "Physics",                FALSE, FALSE, FALSE, { "Education;Science", NULL } },
-  { "Robotics",               FALSE, FALSE, FALSE, { "Education;Science", NULL } },
-  { "Sports",                 FALSE, FALSE, FALSE, { "Education", NULL } },
-  { "ParallelComputing",      FALSE, FALSE, FALSE, { "Education;Science;ComputerScience", NULL } },
-  { "Amusement",              FALSE, FALSE, FALSE, { NULL } },
-  { "Archiving",              FALSE, FALSE, FALSE, { "Utility", NULL } },
-  { "Compression",            FALSE, FALSE, FALSE, { "Utility;Archiving", NULL } },
-  { "Electronics",            FALSE, FALSE, FALSE, { NULL } },
-  { "Emulator",               FALSE, FALSE, FALSE, { "System", "Game", NULL } },
-  { "Engineering",            FALSE, FALSE, FALSE, { NULL } },
-  { "FileTools",              FALSE, FALSE, FALSE, { "Utility", "System", NULL } },
-  { "FileManager",            FALSE, FALSE, FALSE, { "System;FileTools", NULL } },
-  { "TerminalEmulator",       FALSE, FALSE, FALSE, { "System", NULL } },
-  { "Filesystem",             FALSE, FALSE, FALSE, { "System", NULL } },
-  { "Monitor",                FALSE, FALSE, FALSE, { "System", NULL } },
-  { "Security",               FALSE, FALSE, FALSE, { "Settings", "System", NULL } },
-  { "Accessibility",          FALSE, FALSE, FALSE, { "Settings", "Utility", NULL } },
-  { "Calculator",             FALSE, FALSE, FALSE, { "Utility", NULL } },
-  { "Clock",                  FALSE, FALSE, FALSE, { "Utility", NULL } },
-  { "TextEditor",             FALSE, FALSE, FALSE, { "Utility", NULL } },
-  { "Documentation",          FALSE, FALSE, FALSE, { NULL } },
-  { "Core",                   FALSE, FALSE, FALSE, { NULL } },
-  { "KDE",                    FALSE, FALSE, FALSE, { "Qt", NULL } },
-  { "GNOME",                  FALSE, FALSE, FALSE, { "GTK", NULL } },
-  { "GTK",                    FALSE, FALSE, FALSE, { NULL } },
-  { "Qt",                     FALSE, FALSE, FALSE, { NULL } },
-  { "Motif",                  FALSE, FALSE, FALSE, { NULL } },
-  { "Java",                   FALSE, FALSE, FALSE, { NULL } },
-  { "ConsoleOnly",            FALSE, FALSE, FALSE, { NULL } },
-  { "Screensaver",            FALSE, TRUE,  FALSE, { NULL } },
-  { "TrayIcon",               FALSE, TRUE,  FALSE, { NULL } },
-  { "Applet",                 FALSE, TRUE,  FALSE, { NULL } },
-  { "Shell",                  FALSE, TRUE,  FALSE, { NULL } },
-  { "Application",            FALSE, FALSE, TRUE,  { NULL } },
-  { "Applications",           FALSE, FALSE, TRUE,  { NULL } }
+  { "AudioVideo",             TRUE,  FALSE, FALSE, { NULL }, { NULL } },
+  { "Audio",                  TRUE,  FALSE, FALSE, { "AudioVideo", NULL }, { NULL } },
+  { "Video",                  TRUE,  FALSE, FALSE, { "AudioVideo", NULL }, { NULL } },
+  { "Development",            TRUE,  FALSE, FALSE, { NULL }, { NULL } },
+  { "Education",              TRUE,  FALSE, FALSE, { NULL }, { NULL } },
+  { "Game",                   TRUE,  FALSE, FALSE, { NULL }, { NULL } },
+  { "Graphics",               TRUE,  FALSE, FALSE, { NULL }, { NULL } },
+  { "Network",                TRUE,  FALSE, FALSE, { NULL }, { NULL } },
+  { "Office",                 TRUE,  FALSE, FALSE, { NULL }, { NULL } },
+  { "Science",                TRUE,  FALSE, FALSE, { NULL }, { NULL } },
+  { "Settings",               TRUE,  FALSE, FALSE, { NULL }, { NULL } },
+  { "System",                 TRUE,  FALSE, FALSE, { NULL }, { NULL } },
+  { "Utility",                TRUE,  FALSE, FALSE, { NULL }, { NULL } },
+  { "Audio",                  FALSE, FALSE, FALSE, { NULL }, { "Development", NULL } },
+  { "Video",                  FALSE, FALSE, FALSE, { NULL }, { "Development", NULL } },
+  { "Building",               FALSE, FALSE, FALSE, { NULL }, { "Development", NULL } },
+  { "Debugger",               FALSE, FALSE, FALSE, { NULL }, { "Development", NULL } },
+  { "IDE",                    FALSE, FALSE, FALSE, { NULL }, { "Development", NULL } },
+  { "GUIDesigner",            FALSE, FALSE, FALSE, { NULL }, { "Development", NULL } },
+  { "Profiling",              FALSE, FALSE, FALSE, { NULL }, { "Development", NULL } },
+  { "RevisionControl",        FALSE, FALSE, FALSE, { NULL }, { "Development", NULL } },
+  { "Translation",            FALSE, FALSE, FALSE, { NULL }, { "Development", NULL } },
+  { "Calendar",               FALSE, FALSE, FALSE, { NULL }, { "Office", NULL } },
+  { "ContactManagement",      FALSE, FALSE, FALSE, { NULL }, { "Office", NULL } },
+  { "Database",               FALSE, FALSE, FALSE, { NULL }, { "Office", "Development", "AudioVideo", NULL } },
+  { "Dictionary",             FALSE, FALSE, FALSE, { NULL }, { "Office", "TextTools", NULL } },
+  { "Chart",                  FALSE, FALSE, FALSE, { NULL }, { "Office", NULL } },
+  { "Email",                  FALSE, FALSE, FALSE, { NULL }, { "Office", "Network", NULL } },
+  { "Finance",                FALSE, FALSE, FALSE, { NULL }, { "Office", NULL } },
+  { "FlowChart",              FALSE, FALSE, FALSE, { NULL }, { "Office", NULL } },
+  { "PDA",                    FALSE, FALSE, FALSE, { NULL }, { "Office", NULL } },
+  { "ProjectManagement",      FALSE, FALSE, FALSE, { NULL }, { "Office", "Development", NULL } },
+  { "Presentation",           FALSE, FALSE, FALSE, { NULL }, { "Office", NULL } },
+  { "Spreadsheet",            FALSE, FALSE, FALSE, { NULL }, { "Office", NULL } },
+  { "WordProcessor",          FALSE, FALSE, FALSE, { NULL }, { "Office", NULL } },
+  { "2DGraphics",             FALSE, FALSE, FALSE, { NULL }, { "Graphics", NULL } },
+  { "VectorGraphics",         FALSE, FALSE, FALSE, { NULL }, { "Graphics;2DGraphics", NULL } },
+  { "RasterGraphics",         FALSE, FALSE, FALSE, { NULL }, { "Graphics;2DGraphics", NULL } },
+  { "3DGraphics",             FALSE, FALSE, FALSE, { NULL }, { "Graphics", NULL } },
+  { "Scanning",               FALSE, FALSE, FALSE, { NULL }, { "Graphics", NULL } },
+  { "OCR",                    FALSE, FALSE, FALSE, { NULL }, { "Graphics;Scanning", NULL } },
+  { "Photography",            FALSE, FALSE, FALSE, { NULL }, { "Graphics", "Office", NULL } },
+  { "Publishing",             FALSE, FALSE, FALSE, { NULL }, { "Graphics", "Office", NULL } },
+  { "Viewer",                 FALSE, FALSE, FALSE, { NULL }, { "Graphics", "Office", NULL } },
+  { "TextTools",              FALSE, FALSE, FALSE, { NULL }, { "Utility", NULL } },
+  { "DesktopSettings",        FALSE, FALSE, FALSE, { NULL }, { "Settings", NULL } },
+  { "HardwareSettings",       FALSE, FALSE, FALSE, { NULL }, { "Settings", NULL } },
+  { "Printing",               FALSE, FALSE, FALSE, { NULL }, { "HardwareSettings;Settings", NULL } },
+  { "PackageManager",         FALSE, FALSE, FALSE, { NULL }, { "Settings", NULL } },
+  { "Dialup",                 FALSE, FALSE, FALSE, { NULL }, { "Network", NULL } },
+  { "InstantMessaging",       FALSE, FALSE, FALSE, { NULL }, { "Network", NULL } },
+  { "Chat",                   FALSE, FALSE, FALSE, { NULL }, { "Network", NULL } },
+  { "IRCClient",              FALSE, FALSE, FALSE, { NULL }, { "Network", NULL } },
+  { "Feed",                   FALSE, FALSE, FALSE, { NULL }, { "Network", NULL } },
+  { "FileTransfer",           FALSE, FALSE, FALSE, { NULL }, { "Network", NULL } },
+  { "HamRadio",               FALSE, FALSE, FALSE, { NULL }, { "Network", "Audio", NULL } },
+  { "News",                   FALSE, FALSE, FALSE, { NULL }, { "Network", NULL } },
+  { "P2P",                    FALSE, FALSE, FALSE, { NULL }, { "Network", NULL } },
+  { "RemoteAccess",           FALSE, FALSE, FALSE, { NULL }, { "Network", NULL } },
+  { "Telephony",              FALSE, FALSE, FALSE, { NULL }, { "Network", NULL } },
+  { "TelephonyTools",         FALSE, FALSE, FALSE, { NULL }, { "Utility", NULL } },
+  { "VideoConference",        FALSE, FALSE, FALSE, { NULL }, { "Network", NULL } },
+  { "WebBrowser",             FALSE, FALSE, FALSE, { NULL }, { "Network", NULL } },
+  { "WebDevelopment",         FALSE, FALSE, FALSE, { NULL }, { "Network", "Development", NULL } },
+  { "Midi",                   FALSE, FALSE, FALSE, { NULL }, { "AudioVideo;Audio", NULL } },
+  { "Mixer",                  FALSE, FALSE, FALSE, { NULL }, { "AudioVideo;Audio", NULL } },
+  { "Sequencer",              FALSE, FALSE, FALSE, { NULL }, { "AudioVideo;Audio", NULL } },
+  { "Tuner",                  FALSE, FALSE, FALSE, { NULL }, { "AudioVideo;Audio", NULL } },
+  { "TV",                     FALSE, FALSE, FALSE, { NULL }, { "AudioVideo;Video", NULL } },
+  { "AudioVideoEditing",      FALSE, FALSE, FALSE, { NULL }, { "Audio", "Video", "AudioVideo", NULL } },
+  { "Player",                 FALSE, FALSE, FALSE, { NULL }, { "Audio", "Video", "AudioVideo", NULL } },
+  { "Recorder",               FALSE, FALSE, FALSE, { NULL }, { "Audio", "Video", "AudioVideo", NULL } },
+  { "DiscBurning",            FALSE, FALSE, FALSE, { NULL }, { "Audio", "Video", "AudioVideo", NULL } },
+  { "ActionGame",             FALSE, FALSE, FALSE, { NULL }, { "Game", NULL } },
+  { "AdventureGame",          FALSE, FALSE, FALSE, { NULL }, { "Game", NULL } },
+  { "ArcadeGame",             FALSE, FALSE, FALSE, { NULL }, { "Game", NULL } },
+  { "BoardGame",              FALSE, FALSE, FALSE, { NULL }, { "Game", NULL } },
+  { "BlocksGame",             FALSE, FALSE, FALSE, { NULL }, { "Game", NULL } },
+  { "CardGame",               FALSE, FALSE, FALSE, { NULL }, { "Game", NULL } },
+  { "KidsGame",               FALSE, FALSE, FALSE, { NULL }, { "Game", NULL } },
+  { "LogicGame",              FALSE, FALSE, FALSE, { NULL }, { "Game", NULL } },
+  { "RolePlaying",            FALSE, FALSE, FALSE, { NULL }, { "Game", NULL } },
+  { "Shooter",                FALSE, FALSE, FALSE, { NULL }, { "Game", NULL } },
+  { "Simulation",             FALSE, FALSE, FALSE, { NULL }, { "Game", NULL } },
+  { "SportsGame",             FALSE, FALSE, FALSE, { NULL }, { "Game", NULL } },
+  { "StrategyGame",           FALSE, FALSE, FALSE, { NULL }, { "Game", NULL } },
+  { "Art",                    FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "Construction",           FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "Music",                  FALSE, FALSE, FALSE, { NULL }, { "AudioVideo", "Education", NULL } },
+  { "Languages",              FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "ArtificialIntelligence", FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "Astronomy",              FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "Biology",                FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "Chemistry",              FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "ComputerScience",        FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "DataVisualization",      FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "Economy",                FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "Electricity",            FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "Geography",              FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "Geology",                FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "Geoscience",             FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "History",                FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "Humanities",             FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "ImageProcessing",        FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "Literature",             FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "Maps",                   FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", "Utility", NULL } },
+  { "Math",                   FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "NumericalAnalysis",      FALSE, FALSE, FALSE, { NULL }, { "Education;Math", "Science;Math", NULL } },
+  { "MedicalSoftware",        FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "Physics",                FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "Robotics",               FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "Spirituality",           FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", "Utility", NULL } },
+  { "Sports",                 FALSE, FALSE, FALSE, { NULL }, { "Education", "Science", NULL } },
+  { "ParallelComputing",      FALSE, FALSE, FALSE, { NULL }, { "Education;ComputerScience", "Science;ComputerScience", NULL } },
+  { "Amusement",              FALSE, FALSE, FALSE, { NULL }, { NULL } },
+  { "Archiving",              FALSE, FALSE, FALSE, { NULL }, { "Utility", NULL } },
+  { "Compression",            FALSE, FALSE, FALSE, { NULL }, { "Utility;Archiving", NULL } },
+  { "Electronics",            FALSE, FALSE, FALSE, { NULL }, { NULL } },
+  { "Emulator",               FALSE, FALSE, FALSE, { NULL }, { "System", "Game", NULL } },
+  { "Engineering",            FALSE, FALSE, FALSE, { NULL }, { NULL } },
+  { "FileTools",              FALSE, FALSE, FALSE, { NULL }, { "Utility", "System", NULL } },
+  { "FileManager",            FALSE, FALSE, FALSE, { NULL }, { "System;FileTools", NULL } },
+  { "TerminalEmulator",       FALSE, FALSE, FALSE, { NULL }, { "System", NULL } },
+  { "Filesystem",             FALSE, FALSE, FALSE, { NULL }, { "System", NULL } },
+  { "Monitor",                FALSE, FALSE, FALSE, { NULL }, { "System", "Network", NULL } },
+  { "Security",               FALSE, FALSE, FALSE, { NULL }, { "Settings", "System", NULL } },
+  { "Accessibility",          FALSE, FALSE, FALSE, { NULL }, { "Settings", "Utility", NULL } },
+  { "Calculator",             FALSE, FALSE, FALSE, { NULL }, { "Utility", NULL } },
+  { "Clock",                  FALSE, FALSE, FALSE, { NULL }, { "Utility", NULL } },
+  { "TextEditor",             FALSE, FALSE, FALSE, { NULL }, { "Utility", NULL } },
+  { "Documentation",          FALSE, FALSE, FALSE, { NULL }, { NULL } },
+  { "Adult",                  FALSE, FALSE, FALSE, { NULL }, { NULL } },
+  { "Core",                   FALSE, FALSE, FALSE, { NULL }, { NULL } },
+  { "KDE",                    FALSE, FALSE, FALSE, { NULL }, { "Qt", NULL } },
+  { "GNOME",                  FALSE, FALSE, FALSE, { NULL }, { "GTK", NULL } },
+  { "XFCE",                   FALSE, FALSE, FALSE, { NULL }, { "GTK", NULL } },
+  { "GTK",                    FALSE, FALSE, FALSE, { NULL }, { NULL } },
+  { "Qt",                     FALSE, FALSE, FALSE, { NULL }, { NULL } },
+  { "Motif",                  FALSE, FALSE, FALSE, { NULL }, { NULL } },
+  { "Java",                   FALSE, FALSE, FALSE, { NULL }, { NULL } },
+  { "ConsoleOnly",            FALSE, FALSE, FALSE, { NULL }, { NULL } },
+  { "Screensaver",            FALSE, TRUE,  FALSE, { NULL }, { NULL } },
+  { "TrayIcon",               FALSE, TRUE,  FALSE, { NULL }, { NULL } },
+  { "Applet",                 FALSE, TRUE,  FALSE, { NULL }, { NULL } },
+  { "Shell",                  FALSE, TRUE,  FALSE, { NULL }, { NULL } },
+  { "Application",            FALSE, FALSE, TRUE,  { NULL }, { NULL } },
+  { "Applications",           FALSE, FALSE, TRUE,  { NULL }, { NULL } }
 };
 
+/* Escape values for console colors */
+#define UNDERLINE     "\033[4m"
+#define MAGENTA       "\033[35m"
+#define RED           "\033[31m"
+#define YELLOW        "\033[33m"
+
+/* Colour definitions */
+#define RESET_COLOR        (kf->use_colors ? "\033[0m" : "")
+#define FILENAME_COLOR     (kf->use_colors ? UNDERLINE : "")
+#define FATAL_COLOR        (kf->use_colors ? RED : "")
+#define FUTURE_FATAL_COLOR (kf->use_colors ? RED : "")
+#define WARNING_COLOR      (kf->use_colors ? MAGENTA : "")
+#define HINT_COLOR         (kf->use_colors ? YELLOW : "")
+
 static void
 print_fatal (kf_validator *kf, const char *format, ...)
 {
@@ -526,7 +575,9 @@ print_fatal (kf_validator *kf, const char *format, ...)
   str = g_strdup_vprintf (format, args);
   va_end (args);
 
-  g_print ("%s: error: %s", kf->filename, str);
+  g_print ("%s%s%s: %serror%s: %s",
+           FILENAME_COLOR, kf->filename, RESET_COLOR,
+           FATAL_COLOR, RESET_COLOR, str);
 
   g_free (str);
 }
@@ -543,7 +594,9 @@ print_future_fatal (kf_validator *kf, const char *format, ...)
   str = g_strdup_vprintf (format, args);
   va_end (args);
 
-  g_print ("%s: error: (will be fatal in the future): %s", kf->filename, str);
+  g_print ("%s%s%s: %serror%s: (will be fatal in the future): %s",
+           FILENAME_COLOR, kf->filename, RESET_COLOR,
+           FUTURE_FATAL_COLOR, RESET_COLOR, str);
 
   g_free (str);
 }
@@ -560,7 +613,31 @@ print_warning (kf_validator *kf, const char *format, ...)
   str = g_strdup_vprintf (format, args);
   va_end (args);
 
-  g_print ("%s: warning: %s", kf->filename, str);
+  g_print ("%s%s%s: %swarning%s: %s",
+           FILENAME_COLOR, kf->filename, RESET_COLOR,
+           WARNING_COLOR, RESET_COLOR, str);
+
+  g_free (str);
+}
+
+static void
+print_hint (kf_validator *kf, const char *format, ...)
+{
+  va_list args;
+  gchar *str;
+
+  g_return_if_fail (kf != NULL && format != NULL);
+
+  if (kf->no_hints)
+    return;
+
+  va_start (args, format);
+  str = g_strdup_vprintf (format, args);
+  va_end (args);
+
+  g_print ("%s%s%s: %shint%s: %s",
+           FILENAME_COLOR, kf->filename, RESET_COLOR,
+           HINT_COLOR, RESET_COLOR, str);
 
   g_free (str);
 }
@@ -724,9 +801,6 @@ validate_numeric_key (kf_validator *kf,
 /* + Values of type string may contain all ASCII characters except for control
  *   characters.
  *   Checked.
- * + The multiple values should be separated by a semicolon. Those keys which
- *   have several values should have a semicolon as the trailing character.
- *   Checked.
  * + FIXME: how should an empty list be handled?
  */
 static gboolean
@@ -758,24 +832,6 @@ validate_string_regexp_list_key (kf_validator *kf,
     return FALSE;
   }
 
-  if (i > 0 && value[i - 1] != ';') {
-    print_fatal (kf, "value \"%s\" for %s list key \"%s\" in group \"%s\" "
-                     "does not have a semicolon (';') as trailing "
-                     "character\n",
-                     value, type, key, kf->current_group);
-
-    return FALSE;
-  }
-
-  if (i > 1 && value[i - 1] == ';' && value[i - 2] == '\\' &&
-      (i < 3 || value[i - 3] != '\\')) {
-    print_fatal (kf, "value \"%s\" for %s list key \"%s\" in group \"%s\" "
-                     "has an escaped semicolon (';') as trailing character\n",
-                     value, type, key, kf->current_group);
-
-    return FALSE;
-  }
-
   return TRUE;
 }
 
@@ -804,10 +860,6 @@ validate_regexp_list_key (kf_validator *kf,
  * + If a postfixed key occurs, the same key must be also present without the
  *   postfix.
  *   Checked.
- * + The multiple values should be separated by a semicolon. Those keys which
- *   have several values should have a semicolon as the trailing character.
- *   FIXME: partly checked. We use checks that work for sure for ascii
- *   characters, but that could possibly fail in some weird UTF-8 strings.
  * + FIXME: how should an empty list be handled?
  */
 static gboolean
@@ -817,7 +869,6 @@ validate_localestring_list_key (kf_validator *kf,
                                 const char   *value)
 {
   char     *locale_key;
-  int       len;
 
   if (locale)
     locale_key = g_strdup_printf ("%s[%s]", key, locale);
@@ -835,27 +886,6 @@ validate_localestring_list_key (kf_validator *kf,
     return FALSE;
   }
 
-  len = strlen (value);
-
-  if (len > 0 && value[len - 1] != ';') {
-    print_fatal (kf, "value \"%s\" for locale string list key \"%s\" in group "
-                     "\"%s\" does not have a semicolon (';') as trailing "
-                     "character\n",
-                     value, locale_key, kf->current_group);
-
-    return FALSE;
-  }
-
-  if (len > 1 && value[len - 1] == ';' && value[len - 2] == '\\' &&
-      (len < 3 || value[len - 3] != '\\')) {
-    print_fatal (kf, "value \"%s\" for locale string list key \"%s\" in group "
-                     "\"%s\" has an escaped semicolon (';') as trailing "
-                     "character\n",
-                     value, locale_key, kf->current_group);
-
-    return FALSE;
-  }
-
   if (!g_hash_table_lookup (kf->current_keys, key)) {
     print_fatal (kf, "key \"%s\" in group \"%s\" is a localized key, but "
                      "there is no non-localized key \"%s\"\n",
@@ -928,6 +958,18 @@ handle_version_key (kf_validator *kf,
                     const char   *locale_key,
                     const char   *value)
 {
+  if (!strcmp (value, "1.4"))
+    return TRUE;
+
+  if (!strcmp (value, "1.3"))
+    return TRUE;
+
+  if (!strcmp (value, "1.2"))
+    return TRUE;
+
+  if (!strcmp (value, "1.1"))
+    return TRUE;
+
   if (!strcmp (value, "1.0"))
     return TRUE;
 
@@ -964,9 +1006,9 @@ handle_comment_key (kf_validator *kf,
 
   if (keyvalue && g_ascii_strcasecmp (value, keyvalue->value) == 0) {
     print_warning (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
-                       "looks redundant with value \"%s\" of key \"%s\"\n",
+                       "looks the same as that of key \"%s\"\n",
                        value, locale_key, kf->current_group,
-                       keyvalue->value, keyvalue->key);
+                       keyvalue->key);
     return FALSE;
   }
 
@@ -977,9 +1019,9 @@ handle_comment_key (kf_validator *kf,
 
   if (keyvalue && g_ascii_strcasecmp (value, keyvalue->value) == 0) {
     print_warning (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
-                       "looks redundant with value \"%s\" of key \"%s\"\n",
+                       "looks the same as that of key \"%s\"\n",
                        value, locale_key, kf->current_group,
-                       keyvalue->value, keyvalue->key);
+                       keyvalue->key);
     return FALSE;
   }
 
@@ -1021,11 +1063,12 @@ handle_icon_key (kf_validator *kf,
   if (g_str_has_suffix (value, ".png") ||
       g_str_has_suffix (value, ".xpm") ||
       g_str_has_suffix (value, ".svg")) {
-    print_future_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" is an icon "
-                        "name with an extension, but there should be no extension "
-                        "as described in the Icon Theme Specification if the "
-                        "value is not an absolute path\n",
-                        value, locale_key, kf->current_group);
+    print_future_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" is an "
+                            "icon name with an extension, but there should be "
+                            "no extension as described in the Icon Theme "
+                            "Specification if the value is not an absolute "
+                            "path\n",
+                            value, locale_key, kf->current_group);
     return FALSE;
   }
 
@@ -1202,10 +1245,10 @@ handle_exec_key (kf_validator *kf,
                              "a reserved character\n",
                              value, locale_key, kf->current_group);
             retval = FALSE;
-
-            escaped = FALSE;
           }
         }
+
+        escaped = FALSE;
         break;
       case '`':
       case '$':
@@ -1230,6 +1273,16 @@ handle_exec_key (kf_validator *kf,
         break;
       case '\\':
         PRINT_INVALID_IF_FLAG;
+
+        /* Escape character immediately followed by \0? */
+        if (*(c + 1) == '\0') {
+          print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+                           "ends in an incomplete escape sequence\n",
+                           value, locale_key, kf->current_group);
+          retval = FALSE;
+          break;
+        }
+
         c++;
         if (*c == '\\' && in_quote)
           escaped = !escaped;
@@ -1428,10 +1481,10 @@ handle_mime_key (kf_validator *kf,
         break;
       case MU_INVALID:
         print_future_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
-                            "contains value \"%s\" which is an invalid MIME "
-                            "type: %s\n",
-                            value, locale_key, kf->current_group,
-                            types[i], valid_error);
+                                "contains value \"%s\" which is an invalid "
+                                "MIME type: %s\n",
+                                value, locale_key, kf->current_group,
+                                types[i], valid_error);
 
         retval = FALSE;
         g_free (valid_error);
@@ -1462,12 +1515,19 @@ handle_mime_key (kf_validator *kf,
  *   FIXME: it's not really deprecated, so the error message is wrong
  * + All categories extending the format should start with "X-".
  *   Checked.
- * + At least one main category must be included.
+ * + Using multiple main categories may lead to appearing more than once in
+ *   application menu.
+ *   Checked.
+ * + One main category should be included, otherwise application will appear in
+ *   "catch-all" section of application menu.
  *   Checked.
  *   FIXME: decide if it's okay to have an empty list of categories.
- * + Some categories, if include, require that another category is included.
- *   Eg: if Audio is there, AudioVideo must be there. Same for most additional
- *   categories.
+ * + Some categories, if included, require that another category is included.
+ *   Eg: if Audio is there, AudioVideo must be there.
+ *   Checked.
+ * + Some categories, if included, suggest that another category is included.
+ *   Eg: Debugger suggests Development.
+ *   This is the case for most additional categories.
  *   Checked.
  */
 static gboolean
@@ -1480,7 +1540,7 @@ handle_categories_key (kf_validator *kf,
   GHashTable    *hashtable;
   int            i;
   unsigned int   j;
-  gboolean       main_category_present;
+  int            main_categories_nb;
 
   handle_key_for_application (kf, locale_key, value);
 
@@ -1514,7 +1574,7 @@ handle_categories_key (kf_validator *kf,
   }
 
   /* second pass */
-  main_category_present = FALSE;
+  main_categories_nb = 0;
 
   for (i = 0; categories[i]; i++) {
     unsigned int k;
@@ -1541,8 +1601,57 @@ handle_categories_key (kf_validator *kf,
       continue;
     }
 
-    if (registered_categories[j].main)
-      main_category_present = TRUE;
+    if (registered_categories[j].main) {
+      /* only count it as a main category if none of the required categories
+       * for this one is also a main category (and is present) */
+      gboolean required_main_category_present = FALSE;
+
+      for (k = 0; registered_categories[j].requires[k] != NULL; k++) {
+        char **required_categories;
+        int    l;
+
+        required_categories = g_strsplit (registered_categories[j].requires[k],
+                                          ";", 0);
+
+        for (l = 0; required_categories[l]; l++) {
+          unsigned int m;
+
+          if (!g_hash_table_lookup (hashtable, required_categories[l]))
+            continue;
+
+          for (m = 0; m < G_N_ELEMENTS (registered_categories); m++) {
+            if (strcmp (required_categories[l],
+                        registered_categories[m].name) != 0)
+              continue;
+
+            if (registered_categories[m].main)
+              required_main_category_present = TRUE;
+
+            break;
+          }
+
+          if (required_main_category_present)
+            break;
+        }
+
+        if (required_main_category_present) {
+          g_strfreev (required_categories);
+          break;
+        }
+
+        g_strfreev (required_categories);
+      }
+
+      if (!required_main_category_present)
+        main_categories_nb++;
+    }
+
+    if (registered_categories[j].main && main_categories_nb > 1)
+      print_hint (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+                      "contains more than one main category; application "
+                      "might appear more than once in the application menu\n",
+                      value, locale_key, kf->current_group);
+
 
     if (registered_categories[j].deprecated) {
       if (!kf->no_deprecated_warnings)
@@ -1554,7 +1663,7 @@ handle_categories_key (kf_validator *kf,
 
     if (registered_categories[j].require_only_show_in) {
       if (!g_hash_table_lookup (kf->current_keys, "OnlyShowIn")) {
-        print_fatal (kf, "value \"%s\" in key \"%s\" in group \"%s\" "
+        print_fatal (kf, "value item \"%s\" in key \"%s\" in group \"%s\" "
                          "is a reserved category, so a \"OnlyShowIn\" key "
                          "must be included\n",
                          categories[i], locale_key, kf->current_group);
@@ -1562,6 +1671,8 @@ handle_categories_key (kf_validator *kf,
       }
     }
 
+    /* required categories */
+
     for (k = 0; registered_categories[j].requires[k] != NULL; k++) {
       char **required_categories;
       int    l;
@@ -1584,7 +1695,9 @@ handle_categories_key (kf_validator *kf,
       g_strfreev (required_categories);
     }
 
-    /* there was a required category and it wasn't found */
+    /* we've reached the end of a non-empty set of required categories; this
+     * means none of the possible required category (or list of required
+     * categories) was found */
     if (k != 0 && registered_categories[j].requires[k] == NULL) {
       GString *output_required;
 
@@ -1593,27 +1706,73 @@ handle_categories_key (kf_validator *kf,
         g_string_append_printf (output_required, ", or %s",
                                 registered_categories[j].requires[k]);
 
-      print_future_fatal (kf, "value \"%s\" in key \"%s\" in group \"%s\" "
-                          "requires another category to be present among the "
-                          "following categories: %s\n",
-                          categories[i], locale_key, kf->current_group,
-                          output_required->str);
+      print_future_fatal (kf, "value item \"%s\" in key \"%s\" in group \"%s\" "
+                              "requires another category to be present among "
+                              "the following categories: %s\n",
+                              categories[i], locale_key, kf->current_group,
+                              output_required->str);
 
       g_string_free (output_required, TRUE);
       retval = FALSE;
     }
 
+    /* suggested categories */
+
+    for (k = 0; registered_categories[j].suggests[k] != NULL; k++) {
+      char **suggested_categories;
+      int    l;
+
+      suggested_categories = g_strsplit (registered_categories[j].suggests[k],
+                                         ";", 0);
+
+      for (l = 0; suggested_categories[l]; l++) {
+        if (!g_hash_table_lookup (hashtable, suggested_categories[l]))
+          break;
+      }
+
+      /* we've reached the end of a list of suggested categories, so
+       * the condition is satisfied */
+      if (suggested_categories[l] == NULL) {
+        g_strfreev (suggested_categories);
+        break;
+      }
+
+      g_strfreev (suggested_categories);
+    }
+
+    /* we've reached the end of a non-empty set of suggested categories; this
+     * means none of the possible suggested category (or list of suggested
+     * categories) was found */
+    if (k != 0 && registered_categories[j].suggests[k] == NULL) {
+      GString *output_suggested;
+
+      output_suggested = g_string_new (registered_categories[j].suggests[0]);
+      for (k = 1; registered_categories[j].suggests[k] != NULL; k++)
+        g_string_append_printf (output_suggested, ", or %s",
+                                registered_categories[j].suggests[k]);
+
+      print_hint (kf, "value item \"%s\" in key \"%s\" in group \"%s\" "
+                      "can be extended with another category among the "
+                      "following categories: %s\n",
+                      categories[i], locale_key, kf->current_group,
+                      output_suggested->str);
+
+      g_string_free (output_suggested, TRUE);
+    }
+
   }
 
   g_strfreev (categories);
   g_hash_table_destroy (hashtable);
 
-  if (!main_category_present) {
-    print_future_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
-                        "does not contain a registered main category\n",
-                        value, locale_key, kf->current_group, categories[i]);
-    retval = FALSE;
-  }
+  g_assert (main_categories_nb >= 0);
+
+  if (main_categories_nb == 0)
+    print_hint (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+                    "does not contain a registered main category; application "
+                    "might only show up in a \"catch-all\" section of the "
+                    "application menu\n",
+                    value, locale_key, kf->current_group);
 
   return retval;
 }
@@ -1679,6 +1838,46 @@ handle_actions_key (kf_validator *kf,
   return retval;
 }
 
+/* + If the file describes a D-Bus activatable service, the filename must be in
+ *   reverse-DNS notation, i.e. contain at least two dots including the dot
+ *   in ".desktop".
+ *   Checked.
+ */
+static gboolean
+handle_dbus_activatable_key (kf_validator *kf,
+                             const char   *locale_key,
+                             const char   *value)
+{
+  gchar *basename_utf8;
+  gchar *basename;
+  const gchar *p = NULL;
+  gboolean retval = TRUE;
+
+  /* If DBusActivatable=false, don't check */
+  if (strcmp (value, "true") && strcmp (value, "1"))
+    return TRUE;
+
+  basename = g_path_get_basename (kf->filename);
+  basename_utf8 = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
+  if (!basename_utf8)
+    goto out;
+
+  p = g_utf8_strchr (basename_utf8, -1, '.');
+  if (!p)
+    goto out;
+  p = g_utf8_strchr (p + 1, -1, '.');
+
+out:
+  if (!p) {
+    print_fatal (kf, "DBusActivatable filename must conform to reverse-DNS notation\n");
+    retval = FALSE;
+  }
+
+  g_free (basename_utf8);
+  g_free (basename);
+  return retval;
+}
+
 /* + The device to mount. (probably implies an absolute path)
  *   Checked.
  */
@@ -1740,6 +1939,7 @@ handle_encoding_key (kf_validator *kf,
  *   - if-exists FILE
  *   - unless-exists FILE
  *   - DESKTOP-ENVIRONMENT-NAME [DESKTOP-SPECIFIC-TEST]
+ *   - other known conditions (GNOME3, GSettings, etc.)
  *   Checked.
  * + FILE must be a path to a filename, relative to $XDG_CONFIG_HOME.
  *   Checked.
@@ -1801,9 +2001,124 @@ handle_autostart_condition_key (kf_validator *kf,
                          value, locale_key, kf->current_group, argument);
     }
 
+  } else if (strncmp (condition, "X-", 2) == 0) {
+    if (argument && argument[0] == '\0')
+      print_warning (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+                         "has trailing space(s)\n",
+                         value, locale_key, kf->current_group);
   } else {
-    if (strncmp (condition, "X-", 2)) {
-      unsigned int i;
+    unsigned int i;
+    unsigned int j;
+
+    /* Look if it's a registered AutostartCondition */
+
+    for (i = 0; i < G_N_ELEMENTS (registered_autostart_condition); i++) {
+
+      if (strcmp (condition, registered_autostart_condition[i].name) != 0)
+        continue;
+
+      /* check if first argument is one of the expected ones */
+      for (j = 0; registered_autostart_condition[i].first_arg[j] != NULL; j++) {
+        const char *first = registered_autostart_condition[i].first_arg[j];
+        char       *after_first = argument;
+
+        if (argument && !strncmp (argument, first, strlen (first))) {
+          after_first += strlen (first);
+          if (after_first[0] == '\0' || after_first[0] == ' ') {
+            /* find next argument */
+            argument = after_first;
+            while (*argument == ' ')
+              argument++;
+          }
+
+          break;
+        }
+      }
+
+      /* we've reached the end of a non-empty set of first arguments; this
+       * means none of the possible first arguments was found */
+      if (j != 0 && registered_autostart_condition[i].first_arg[j] == NULL) {
+        GString *output;
+
+        output = g_string_new (registered_autostart_condition[i].first_arg[0]);
+        for (j = 1; registered_autostart_condition[i].first_arg[j] != NULL; j++)
+          g_string_append_printf (output, ", or %s",
+                                  registered_autostart_condition[i].first_arg[j]);
+
+        print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+                         "does not contain a valid first argument for "
+                         "condition \"%s\"; valid first arguments are: %s\n",
+                         value, locale_key, kf->current_group,
+                         condition, output->str);
+        retval = FALSE;
+
+        g_string_free (output, TRUE);
+
+      } else {
+
+        switch (registered_autostart_condition[i].additional_args) {
+          case 0:
+            if (argument && argument[0] != '\0') {
+              print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+                               "has too many arguments for condition \"%s\"\n",
+                               value, locale_key, kf->current_group, condition);
+              retval = FALSE;
+            }
+            break;
+
+          case 1:
+            /* we handle the "one argument" case specially, as spaces might be
+             * normal there, and therefore we don't want to split the string
+             * based on spaces */
+            if (!argument || argument[0] == '\0') {
+              print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+                               "is missing a last argument for condition "
+                               "\"%s\"\n",
+                               value, locale_key, kf->current_group, condition);
+              retval = FALSE;
+            }
+            break;
+
+          default:
+            {
+              int argc_diff = -registered_autostart_condition[i].additional_args;
+
+              while (argument && argument[0] != '\0') {
+                argc_diff++;
+                argument = g_utf8_strchr (argument, -1, ' ');
+                while (argument && *argument == ' ')
+                  argument++;
+              }
+
+              if (argc_diff > 0) {
+                print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+                                 "has %d too many arguments for condition "
+                                 "\"%s\"\n",
+                                 value, locale_key, kf->current_group,
+                                 argc_diff, condition);
+                retval = FALSE;
+              } else if (argc_diff < 0) {
+                print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+                                 "has %d too few arguments for condition "
+                                 "\"%s\"\n",
+                                 value, locale_key, kf->current_group,
+                                 -argc_diff, condition);
+                retval = FALSE;
+              }
+            }
+            break;
+        }
+
+      }
+
+      break;
+
+    }
+
+    /* Now, if we didn't find condition in list of registered
+     * AutostartCondition... */
+    if (i == G_N_ELEMENTS (registered_autostart_condition)) {
+      /* Accept conditions with same name as OnlyShowIn values */
 
       for (i = 0; i < G_N_ELEMENTS (show_in_registered); i++) {
         if (!strcmp (condition, show_in_registered[i]))
@@ -1818,12 +2133,11 @@ handle_autostart_condition_key (kf_validator *kf,
                          value, locale_key, kf->current_group, condition);
         retval = FALSE;
       }
-    }
 
-    if (argument && argument[0] == '\0') {
-      print_warning (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
-                         "has trailing space(s)\n",
-                         value, locale_key, kf->current_group);
+      if (argument && argument[0] == '\0')
+        print_warning (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+                           "has trailing space(s)\n",
+                           value, locale_key, kf->current_group);
     }
   }
 
@@ -1946,9 +2260,6 @@ validate_known_key (kf_validator         *kf,
   unsigned int i;
   unsigned int j;
 
-  if (!strncmp (key, "X-", 2))
-    return TRUE;
-
   for (i = 0; i < n_keys; i++) {
     if (strcmp (key, keys[i].name))
       continue;
@@ -1956,6 +2267,8 @@ validate_known_key (kf_validator         *kf,
     if (keys[i].type != DESKTOP_LOCALESTRING_TYPE &&
         keys[i].type != DESKTOP_LOCALESTRING_LIST_TYPE &&
         locale != NULL) {
+      if (!strncmp (key, "X-", 2))
+        return TRUE;
       print_fatal (kf, "file contains key \"%s\" in group \"%s\", "
                        "but \"%s\" is not defined as a locale string\n",
                        locale_key, kf->current_group, key);
@@ -1978,6 +2291,9 @@ validate_known_key (kf_validator         *kf,
                          "KDE\n",
                          locale_key, kf->current_group);
 
+    if (!strncmp (key, "X-", 2))
+      return TRUE;
+
     if (!validate_for_type[j].validate (kf, key, locale, value))
       return FALSE;
 
@@ -1989,7 +2305,7 @@ validate_known_key (kf_validator         *kf,
     break;
   }
 
-  if (i == n_keys) {
+  if (i == n_keys && strncmp (key, "X-", 2)) {
     print_fatal (kf, "file contains key \"%s\" in group \"%s\", but "
                      "keys extending the format should start with "
                      "\"X-\"\n", key, kf->current_group);
@@ -2778,7 +3094,8 @@ groups_hashtable_free (gpointer key,
 gboolean
 desktop_file_validate (const char *filename,
                        gboolean    warn_kde,
-                       gboolean    no_warn_deprecated)
+                       gboolean    no_warn_deprecated,
+                       gboolean    no_hints)
 {
   kf_validator kf;
 
@@ -2795,6 +3112,7 @@ desktop_file_validate (const char *filename,
   kf.current_keys           = NULL;
   kf.kde_reserved_warnings  = warn_kde;
   kf.no_deprecated_warnings = no_warn_deprecated;
+  kf.no_hints               = no_hints;
 
   kf.main_group       = NULL;
   kf.type             = INVALID_TYPE;
@@ -2809,6 +3127,11 @@ desktop_file_validate (const char *filename,
   kf.action_groups    = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                NULL, g_free);
   kf.fatal_error      = FALSE;
+#if GLIB_CHECK_VERSION(2, 50, 0)
+  kf.use_colors       = g_log_writer_supports_color (fileno (stdout));
+#else
+  kf.use_colors       = FALSE;
+#endif
 
   validate_load_and_parse (&kf);
   //FIXME: this does not work well if there are both a Desktop Entry and a KDE
@@ -2844,45 +3167,11 @@ desktop_file_validate (const char *filename,
   return (!kf.fatal_error);
 }
 
-static void
-fixup_list (GKeyFile    *keyfile,
-            const gchar *filename,
-            const gchar *key)
-{
-  char *value;
-  int   len;
-
-  value = g_key_file_get_value (keyfile, GROUP_DESKTOP_ENTRY, key, NULL);
-  if (!value)
-    return;
-
-  len = strlen (value);
-
-  if (len > 0 && (value[len - 1] != ';' ||
-                  (len > 1 && value[len - 2] == '\\' &&
-                  (len < 3 || value[len - 3] != '\\')))) {
-    char *str;
-
-    g_printerr ("%s: warning: key \"%s\" is a list and does not have a "
-                "semicolon as trailing character, fixing\n",
-                filename, key);
-
-    str = g_strconcat (value, ";", NULL);
-    g_key_file_set_value (keyfile, GROUP_DESKTOP_ENTRY,
-                          key, str);
-    g_free (str);
-  }
-}
-
 /* return FALSE if we were unable to fix the file */
 gboolean
 desktop_file_fixup (GKeyFile   *keyfile,
                     const char *filename)
 {
-  gchar        **keys;
-  gsize          keys_nb;
-  unsigned int   i;
-
   if (g_key_file_has_group (keyfile, GROUP_KDE_DESKTOP_ENTRY)) {
     g_printerr ("%s: warning: renaming deprecated \"%s\" group to \"%s\"\n",
                 filename, GROUP_KDE_DESKTOP_ENTRY, GROUP_DESKTOP_ENTRY);
@@ -2890,29 +3179,5 @@ desktop_file_fixup (GKeyFile   *keyfile,
                                GROUP_KDE_DESKTOP_ENTRY, GROUP_DESKTOP_ENTRY);
   }
 
-  keys = g_key_file_get_keys (keyfile, GROUP_DESKTOP_ENTRY, &keys_nb, NULL);
-
-  /* Fix lists to have a ';' at the end if they don't */
-  for (i = 0; i < G_N_ELEMENTS (registered_desktop_keys); i++) {
-    if (registered_desktop_keys[i].type == DESKTOP_STRING_LIST_TYPE ||
-        registered_desktop_keys[i].type == DESKTOP_REGEXP_LIST_TYPE)
-      fixup_list (keyfile, filename, registered_desktop_keys[i].name);
-
-    if (registered_desktop_keys[i].type == DESKTOP_LOCALESTRING_LIST_TYPE) {
-      gsize keylen;
-      guint j;
-
-      keylen = strlen (registered_desktop_keys[i].name);
-      for (j = 0; j < keys_nb; j++) {
-        if (g_str_has_prefix (keys[j], registered_desktop_keys[i].name) &&
-            (keys[j][keylen] == '[' || keys[j][keylen] == '\0')) {
-          fixup_list (keyfile, filename, keys[j]);
-        }
-      }
-    }
-  }
-
-  g_strfreev (keys);
-
   return TRUE;
 }