Index: css/style.css
===================================================================
--- css/style.css	(revision 528)
+++ css/style.css	(working copy)
@@ -58,17 +58,17 @@
 	line-height: 1.5em;
 	width: 40em;
 }	
-table.translations {
+table.translations, table.translation-sets {
 	font-size: 90%;
 	width: 100%;
 	border-spacing: 0.1em;
 }
-table.translations th {
+table.translations th, table.translation-sets th {
 	font-weight: bold;
 	color: #eee;
 	background-color: #555;	
 }
-table.translations tr {
+table.translations tr, table.translation-sets tr {
 	border: 0;
 }
 table.translations td.translation ul {
@@ -93,7 +93,7 @@
     font-style: italic;
 }
 
-table.translations td, table.translations th {
+table.translations td, table.translations th, table.translation-sets td, table.translation-sets th {
 	padding: 0.5em;
 	border: 1px solid #eee;
 	/* white-space: pre-wrap; */
@@ -414,9 +414,9 @@
 }
 p.description {
 	max-width: 60em;
-	margin-left: 1em;
-	padding-left: 1em;
-	border-left: 3px solid #aaa;
+	margin: 0 0 0 1em;
+	/*padding-left: 1em;
+	border-left: 3px solid #aaa;*/
 }
 span.added {
     color: green;
@@ -441,4 +441,64 @@
 .text {
 	width: 45em;
 	margin-left: 6em;
+}
+
+#sub-projects h2, #translation-sets h2 {
+	font-weight: normal;
+	font-size: 1.2em;
+}
+
+#sub-projects {
+	float: left;
+	min-width: 300px;
+}
+#sub-projects h2 {
+	margin-right: 1em; /* Make the break between columns more obvious */
+}
+
+.sub-projects {
+	list-style: none;
+	padding: 0;
+}
+.sub-projects a {
+	font-weight: bold;
+	color: #000;
+	text-decoration: none;
+}
+.sub-projects a.action {
+	font-weight: normal;
+}
+
+#translation-sets {
+	min-width: 500px;
+	width: 50%;
+	float: left;
+}
+
+.translation-sets .percent {
+	font-weight: bold;
+}
+.translation-sets .stats {
+	text-align: center;
+}
+.translation-sets tr {
+	background-color: #eee;
+}
+.translation-sets tr.alt {
+	background-color: #fff;
+}
+.translation-sets tr:hover {
+	background-color: #dee;
+}
+.translation-sets tr.alt:hover {
+	background-color: #eff;
+}
+
+.tag {
+	font-size: 80%;
+	padding: 0.2em 0.2em 0.1em;
+	text-decoration: none;
+	-webkit-border-radius: 0.2em;
+	-moz-border-radius: 0.2em;
+	text-align: center;
 }
\ No newline at end of file
Index: gp-includes/tags.php
===================================================================
--- gp-includes/tags.php	(revision 0)
+++ gp-includes/tags.php	(revision 0)
@@ -0,0 +1,46 @@
+<?php
+
+class GP_Tag {
+	var $name;
+	var $callback;
+	var $color;
+	var $text_color;
+
+	var $action;
+	var $action_num_args = 0;
+
+	function __construct( $name, $callback, $color = '#ff0', $text_color = '#000' ) {
+		$this->name = $name;
+		$this->callback = $callback;
+		$this->color = $color;
+		$this->text_color = $text_color;
+
+		if ( !empty( $this->action ) )
+			add_action( $this->action, array( &$this, 'display' ), 10, $this->action_num_args );
+	}
+
+	function display() {
+		$args = func_get_args();
+
+		if ( call_user_func_array( $this->callback, $args ) ) {
+			echo ' <span class="tag" style="background-color: ', $this->color, '; color: ', $this->text_color, '">', $this->name, '</span>';
+		}
+	}
+}
+
+class GP_Translation_Set_Tag extends GP_Tag {
+	var $action = 'project_template_translation_set_extra';
+	var $action_num_args = 2;
+}
+
+class GP_Default_Tags {
+	function __construct() {
+		add_action( 'init', array( &$this, 'init' ) );
+	}
+
+	function init() {
+		new GP_Translation_Set_Tag( __( '90%+' ), lambda( '$set, $project', '$project->original_count() && $set->current_count() / $project->original_count() >= .9' ), '#070', '#fff' );
+		new GP_Translation_Set_Tag( __( 'My language' ), lambda( '$set, $project', 'GP::$user->logged_in() && $set->locale == GP::$user->current()->get_meta( "language" )' ) );
+	}
+}
+new GP_Default_Tags;
\ No newline at end of file

Property changes on: gp-includes\tags.php
___________________________________________________________________
Added: svn:eol-style
   + native

Index: gp-includes/template.php
===================================================================
--- gp-includes/template.php	(revision 528)
+++ gp-includes/template.php	(working copy)
@@ -81,6 +81,17 @@
 	}
 }
 
+function gp_breadcrumb_project( $project ) {
+	$ret = array();
+
+	while ( $project ) {
+		array_unshift( $ret, gp_link_project_get( $project, $project->name ) );
+		$project = $project->parent_project_id ? GP::$project->get( $project->parent_project_id ) : null;
+	}
+
+	return $ret;
+}
+
 function gp_js_focus_on( $html_id ) {
 	return '<script type="text/javascript">document.getElementById("'.$html_id.'").focus();</script>';
 }
@@ -217,4 +228,21 @@
 function gp_array_of_array_of_things_to_json( $array ) {
 	$map_to_fields = create_function( '$array', 'return array_map( lambda( \'$thing\', \'$thing->fields();\' ), $array );' );
 	return json_encode( array_map( $map_to_fields, $array ) );
-}
\ No newline at end of file
+}
+
+function get_alt_class( $key, $others = '' ) {
+	global $gp_alt;
+	$class = '';
+	if ( !isset( $gp_alt[$key] ) ) $gp_alt[$key] = -1;
+	++$gp_alt[$key];
+	$others = trim($others);
+	if ( $others xor $gp_alt[$key] % 2 )
+		$class = ' class="' . ( ($others) ? $others : 'alt' ) . '"';
+	elseif ( $others && $gp_alt[$key] % 2 )
+		$class = ' class="' . $others . ' alt"';
+	return $class;
+}
+
+function alt_class( $key, $others = '' ) {
+	echo get_alt_class( $key, $others );
+}
Index: gp-includes/things/project.php
===================================================================
--- gp-includes/things/project.php	(revision 528)
+++ gp-includes/things/project.php	(working copy)
@@ -147,5 +147,9 @@
 		}
 		return compact( 'added', 'removed' );
 	}
+
+	function original_count() {
+		return count( GP::$original->by_project_id( $this->id ) );
+	}
 }
 GP::$project = new GP_Project();
\ No newline at end of file
Index: gp-includes/things/translation-set.php
===================================================================
--- gp-includes/things/translation-set.php	(revision 528)
+++ gp-includes/things/translation-set.php	(working copy)
@@ -144,5 +144,9 @@
 		); 
 	}
 
+	function percent_translated() {
+		$original_count = count( GP::$original->by_project_id( $this->project_id ) );
+		return sprintf( _x( '%d%%', 'language translation percent' ), $original_count ? $this->current_count() / $original_count * 100 : 0 );
+	}
 }
 GP::$translation_set = new GP_Translation_Set();
Index: gp-settings.php
===================================================================
--- gp-settings.php	(revision 528)
+++ gp-settings.php	(working copy)
@@ -176,6 +176,7 @@
 
 require_once( GP_PATH . GP_INC . 'template.php' );
 require_once( GP_PATH . GP_INC . 'template-links.php' );
+require_once( GP_PATH . GP_INC . 'tags.php' );
 
 require_once( GP_PATH . GP_INC . 'cli.php' );
 
Index: gp-templates/project-edit.php
===================================================================
--- gp-templates/project-edit.php	(revision 528)
+++ gp-templates/project-edit.php	(working copy)
@@ -1,8 +1,6 @@
 <?php
 gp_title( sprintf( __( 'Edit Project %s &lt; GlotPress' ),  $project->name ) );
-gp_breadcrumb( array(
-	gp_link_project_get( $project, $project->name ),
-) );
+gp_breadcrumb( gp_breadcrumb_project( $project ) );
 gp_tmpl_header();
 ?>
 <h2><?php _e( sprintf( __('Edit project <q>%s</q>'), esc_html( $project->name ) ) ); ?></h2>
Index: gp-templates/project-import.php
===================================================================
--- gp-templates/project-import.php	(revision 528)
+++ gp-templates/project-import.php	(working copy)
@@ -2,10 +2,9 @@
 gp_title( $kind == 'originals'?
  	sprintf( __('Import Originals &lt; %s &lt; GlotPress'), esc_html( $project->name ) ) :
 	sprintf( __('Import Translations &lt; %s &lt; GlotPress'), esc_html( $project->name ) ) );
-gp_breadcrumb( array(
-	gp_link_project_get( $project, $project->name ),
+gp_breadcrumb( array_merge( gp_breadcrumb_project( $project ), array(
 	$kind == 'originals'? __('Import Originals') : __('Import Translations'),
-) );
+) ) );
 gp_tmpl_header();
 ?>
 <form action="" method="post" enctype="multipart/form-data">
Index: gp-templates/project-mass-create-sets.php
===================================================================
--- gp-templates/project-mass-create-sets.php	(revision 528)
+++ gp-templates/project-mass-create-sets.php	(working copy)
@@ -1,8 +1,6 @@
 <?php
 gp_title( sprintf( __( 'Mass-create Translation Sets &lt; %s &lt; GlotPress' ),  $project->name ) );
-gp_breadcrumb( array(
-	gp_link_project_get( $project, $project->name ),
-) );
+gp_breadcrumb( gp_breadcrumb_project( $project ) );
 wp_enqueue_script( 'mass-create-sets-page' );
 wp_localize_script( 'mass-create-sets-page', '$gp_mass_create_sets_options', array(
 	'url' => gp_url_join( gp_url_current(), 'preview'),
Index: gp-templates/project-permissions.php
===================================================================
--- gp-templates/project-permissions.php	(revision 528)
+++ gp-templates/project-permissions.php	(working copy)
@@ -1,9 +1,8 @@
 <?php
 gp_title( sprintf( __( 'Permissions &lt; %s &lt; GlotPress' ), $project->name ) );
-gp_breadcrumb( array(
-	gp_link_project_get( $project, $project->name ),
+gp_breadcrumb( array_merge( gp_breadcrumb_project( $project ), array(
 	__('Permissions')
-) );
+) ) );
 gp_tmpl_header();
 ?>
 <h2><?php _e('Permissions'); ?></h2>
@@ -28,7 +27,7 @@
 			<span class="user"><?php echo esc_html( $permission->set_slug ); ?></span>
 			<a href="<?php echo gp_url_join( gp_url_current(), '-delete/'.$permission->id ); ?>" class="action delete"><?php _e('Remove'); ?></a>
 		</li>
-	<? endforeach; ?>
+	<?php endforeach; ?>
 </ul>	
 	<?php endif; ?>
 	<?php  if ( $parent_permissions ): ?>
@@ -45,7 +44,7 @@
 				<span class="permission-action">in the project </span>
 				<span class="user"><?php gp_link_project( $permission->project, esc_html( $permission->project->name ) ); ?></span>
 			</li>
-		<? endforeach; ?>
+		<?php endforeach; ?>
 </ul>				
 	<?php endif; ?>
 	<?php if ( !$permissions && !$parent_permissions ): ?>
Index: gp-templates/project.php
===================================================================
--- gp-templates/project.php	(revision 528)
+++ gp-templates/project.php	(working copy)
@@ -1,58 +1,88 @@
 <?php
 gp_title( sprintf( __('%s &lt; GlotPress'), esc_html( $project->name ) ) );
-gp_breadcrumb( array(
-	// TODO: add parent projects to breadcrumb
-	gp_link_project_get( $project, $project->name ),
-) );
+gp_breadcrumb( gp_breadcrumb_project( $project ) );
 wp_enqueue_script( 'common' );
 gp_tmpl_header();
 ?>
 <p class="description">
 	<?php echo $project->description; ?>
-	<span class="secondary"><?php if ( $can_write ) gp_link_project_edit( $project, __('Edit project') ); ?></span>
 </p>
+
+<?php if ( $can_write ): ?>
+	<p id="project-actions" class="secondary actionlist">
+		<?php gp_link_project_edit( $project, __('Edit project') ); ?> &bull;
+		<?php gp_link( gp_url_project( $project, 'import-originals' ), __( 'Import originals' ) ); ?> &bull;
+		<?php gp_link( gp_url_project( $project, array( '-permissions' ) ), __('Permissions') ); ?> &bull;
+		<?php gp_link( gp_url_project( '', '-new', array('parent_project_id' => $project->id) ), __('New Sub-Project') ); ?> &bull;
+		<?php gp_link( gp_url( '/sets/-new', array( 'project_id' => $project->id ) ), __('New Translation Set') ); ?> &bull;
+		<?php gp_link( gp_url_project( $project, array( '-mass-create-sets' ) ), __('Mass-create Translation Sets') ); ?>
+	</p>
+<script type="text/javascript">
+jQuery(function($){
+	var project_actions = $('<select/>').html('<option value=""><?php echo addslashes( __( 'Project actions' ) ); ?></option>').change(function(){
+		if ($(this).val())
+			location.href = $(this).val();
+	});
+	$('#project-actions').children('a').each(function(){
+		project_actions.append($('<option/>').text($(this).text()).val($(this).attr('href')));
+	}).end().replaceWith(project_actions);
+});
+</script>
+<div class="clear"></div>
+<?php endif; ?>
+
+<div id="sub-projects">
 <?php if ($sub_projects): ?>
-<p class="secondary"><?php printf( __('Sub-projects of %s:'), $project->name ); ?></p>
-<ul>
+<h2><?php printf( __('Sub-projects of %s:'), $project->name ); ?></h2>
+<ul class="sub-projects">
 <?php foreach($sub_projects as $sub_project): ?>
 	<li>
-		<?php gp_link_project( $sub_project, esc_html( $sub_project->name )); ?>			
-		<?php gp_link_project_edit( $sub_project ); ?>			
+		<?php gp_link_project( $sub_project, esc_html( $sub_project->name )); ?>
+		<?php gp_link_project_edit( $sub_project ); ?>
 		<?php gp_link_project_delete( $sub_project ); ?>
+		<p class="secondary description"><?php echo $sub_project->description; ?></p>
 	</li>
 <?php endforeach; ?>
-</ul>	
+</ul>
 <?php endif; ?>
+</div>
+	<div id="translation-sets">
 <?php if ( $translation_sets ): ?>
-	<?php _e('Translations:'); ?>
-	<ul class="translation-sets">
+	<h2><?php _e('Translations:'); ?></h2>
+	<table class="translation-sets">
+	<thead>
+		<tr>
+			<th><?php _e( 'Language' ); ?></th>
+			<th><?php echo _x( '%', 'language translation percent header' ); ?></th>
+			<th><?php _e( 'Translated' ); ?></th>
+			<th><?php _e( 'Untranslated' ); ?></th>
+			<th><?php _e( 'Waiting' ); ?></th>
+		</tr>
+	</thead>
+	<tbody>
 	<?php foreach( $translation_sets as $set ): ?>    
-		<li>
-			<?php gp_link( gp_url_project( $project, gp_url_join( $set->locale, $set->slug ) ), $set->name_with_locale() ); ?>
-			<span class="stats secondary">
-				<!--
-				<span class="translated" title="translated"><?php echo $set->current_count(); ?></span>
-				<span class="untranslated" title="untranslated"><?php echo $set->untranslated_count(); ?></span>
-				-->
-			<?php if ( GP::$user->can( 'approve', 'translation-set', $set->id ) && $waiting = $set->waiting_count() ): ?>
-				<?php gp_link( gp_url_project( $project, gp_url_join( $set->locale, $set->slug ),
-						array('filters[translated]' => 'yes', 'filters[status]' => 'waiting') ), $waiting, array('class' => 'waiting', 'title' => 'waiting') ); ?>
-			<?php endif; ?>
-			<?php if ( GP::$user->can( 'approve', 'translation-set', $set->id ) && $warnings = $set->warnings_count() ): ?>
-				<?php gp_link( gp_url_project( $project, gp_url_join( $set->locale, $set->slug ),
-						array('filters[translated]' => 'yes', 'filters[warnings]' => 'yes' ) ), $warnings, array('class' => 'warnings', 'title' => 'with warnings') ); ?>
-			<?php endif; ?>
-			
-			<?php do_action( 'project_template_translation_set_extra', $set, $project ); ?>
-			</span>
-		</li>
+		<tr<?php echo alt_class( 'set' ); ?>>
+			<td>
+				<strong><?php gp_link( gp_url_project( $project, gp_url_join( $set->locale, $set->slug ) ), $set->name_with_locale() ); ?></strong>
+				<?php do_action( 'project_template_translation_set_extra', $set, $project ); ?>
+			</td>
+			<td class="stats percent"><?php echo $set->percent_translated(); ?></td>
+			<td class="stats translated" title="translated"><?php gp_link( gp_url_project( $project, gp_url_join( $set->locale, $set->slug ),
+						array('filters[translated]' => 'yes', 'filters[status]' => 'current') ), $set->current_count() );; ?></td>
+			<td class="stats untranslated" title="untranslated"><?php gp_link( gp_url_project( $project, gp_url_join( $set->locale, $set->slug ),
+						array('filters[translated]' => 'no', 'filters[status]' => 'either') ), $set->untranslated_count() ); ?></td>
+			<td class="stats waiting"><?php gp_link( gp_url_project( $project, gp_url_join( $set->locale, $set->slug ),
+						array('filters[translated]' => 'yes', 'filters[status]' => 'waiting') ), $set->waiting_count() ); ?></td>
+		</tr>
 	<?php endforeach; ?>
-	</ul>
+	</tbody>
+	</table>
 <?php elseif ( !$sub_projects ): ?>
 	<p><?php _e('There are no translations of this project.'); ?></p>
 <?php endif; ?>
+	</div>
 <?php if ( $can_write && $translation_sets ): ?>
-	<div class="secondary actionlist">
+	<div class="secondary actionlist clear">
 	<a href="#" class="personal-options" id="personal-options-toggle"><?php _e('Personal project options &darr;'); ?></a>
 	<div class="personal-options">
 		<form action="<?php echo gp_url_project( $project, '-personal' ); ?>" method="post">
@@ -71,15 +101,7 @@
 	</div>
 	</div>
 <?php endif; ?>
-<?php if ( $can_write ): ?>
-	<p class="secondary actionlist">
-		<?php gp_link( gp_url_project( $project, 'import-originals' ), __( 'Import originals' ) ); ?> &bull;
-		<?php gp_link( gp_url_project( $project, array( '-permissions' ) ), __('Permissions') ); ?> &bull;
-		<?php gp_link( gp_url_project( '', '-new', array('parent_project_id' => $project->id) ), __('New Sub-Project') ); ?> &bull;
-		<?php gp_link( gp_url( '/sets/-new', array( 'project_id' => $project->id ) ), __('New Translation Set') ); ?> &bull;
-		<?php gp_link( gp_url_project( $project, array( '-mass-create-sets' ) ), __('Mass-create Translation Sets') ); ?>
-	</p>
-<?php endif; ?>
+<div class="clear"></div>
 <script type="text/javascript" charset="utf-8">
 	$gp.showhide('a.personal-options', 'Personal project options &darr;', 'Personal project options &uarr;', 'div.personal-options', '#source-url-template');
 	$('div.personal-options').hide();
Index: gp-templates/translations.php
===================================================================
--- gp-templates/translations.php	(revision 528)
+++ gp-templates/translations.php	(working copy)
@@ -1,9 +1,8 @@
 <?php
 gp_title( sprintf( __( 'Translations &lt; %s &lt; %s &lt; GlotPress' ), $translation_set->name, $project->name ) );
-gp_breadcrumb( array(
-	gp_link_project_get( $project, $project->name ),
-	gp_link_get( $url, $locale->english_name . 'default' != $translation_set->slug? ' '.$translation_set->name : '' ),
-) );
+gp_breadcrumb( array_merge( gp_breadcrumb_project( $project ), array(
+	gp_link_get( $url, $locale->english_name . 'default' != $translation_set->slug ? ' '.$translation_set->name : '' ),
+) ) );
 wp_enqueue_script( 'editor' );
 wp_enqueue_script( 'translations-page' );
 // localizer adds var in front of the variable name, so we can't use $gp.editor.options

