[PATCH] Group photos by import path



Hi,

Patch attached adds support to show the photos grouped by Import path in
the Icon view. For a screenshot, see: 
www.geocities.com/patanjalisomayaji/Screenshot.png

This being an initial implementation, the patch still needs a lot of
cleanups: 
0. General cleanup and possible rewrite of DrawAllCells (),
GetStartPhoto(), and GetEndPhoto ().
1. Scroll on idle handler is currently commented out. 
2. Code for rendering the import path text needs rewriting. Currently,
it: 
	i. calculates a height for the string "Hello, world".
	ii. uses that for calculations in the exposed event. 

cheers,
Patanjali

Index: src/IconView.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/IconView.cs,v
retrieving revision 1.20
diff -u -r1.20 IconView.cs
--- src/IconView.cs	3 Apr 2004 07:41:02 -0000	1.20
+++ src/IconView.cs	3 Jun 2004 09:48:03 -0000
@@ -55,12 +55,18 @@
 		}
 	}
 
-
 	// Size of the frame around the thumbnail.
 	private const int CELL_BORDER_WIDTH = 10;
 
 	// Border around the scrolled area.
 	private const int BORDER_SIZE = 6;
+	
+	// space around text of path
+	private const int BORDER_ABOVE_PATHNAME = 18;
+	private const int BORDER_BELOW_PATHNAME = 6;
+
+	// space after photos of each import path
+	private const int BORDER_END_IMPORT_PATH = 6;
 
 	// Thickness of the outline used to indicate selected items. 
 	private const int SELECTION_THICKNESS = 3;
@@ -81,10 +87,37 @@
 	private int cells_per_row;
 	private int cell_width;
 	private int cell_height;
+	private int path_height;
 
 	// Query we are displaying.
 	private PhotoQuery query;
 
+	// store information about import paths 
+	struct ImportPathInfo 
+	{
+		private int count;
+		private string pathname;
+
+		public int Count {
+			get {
+				return count;
+			}
+			set {		
+				count = value; 
+			}
+		}
+		public string Pathname {
+			get {
+				return pathname;
+			}
+			set {
+				pathname = value;
+			}
+		}
+	}
+	
+	private ArrayList import_path_info;
+		
 	// The first pixel line that is currently on the screen (i.e. in the current
 	// scroll region).  Used to compute the area that went offscreen in the "changed"
 	// signal handler for the vertical GtkAdjustment.
@@ -125,6 +158,8 @@
 
 		selected_cells = new Hashtable ();
 
+		import_path_info = new ArrayList ();
+
 		ScrollAdjustmentsSet += new ScrollAdjustmentsSetHandler (HandleScrollAdjustmentsSet);
 		SizeAllocated += new SizeAllocatedHandler (HandleSizeAllocated);
 		ExposeEvent += new ExposeEventHandler (HandleExposeEvent);
@@ -146,12 +181,14 @@
 		// FIXME we should probably try to merge the selection forward
 		// but it needs some thought to be efficient.
 		UnselectAllCells ();
+		UpdateImportPathInfo ();
 		QueueResize ();
 	}
 
 	public IconView (PhotoQuery query) : this ()
 	{
 		this.query = query;
+		UpdateImportPathInfo ();
 		query.Reload += new PhotoQuery.ReloadHandler (OnReload);
 	}
 
@@ -197,24 +234,54 @@
 		return IconView.error_pixbuf;
 	}
 
-	private int CellAtPosition (int x, int y)
+	private int CellAtPosition (int x, int y) 
 	{
-		if (query == null)
+		if (query == null) 
 			return -1;
-
-		if (x < BORDER_SIZE || x >= BORDER_SIZE + cells_per_row * cell_width)
+		
+		if (x < BORDER_SIZE * 2 || x >= cells_per_row * cell_width)
 			return -1;
-		if (y < BORDER_SIZE || y >= BORDER_SIZE + (query.Photos.Length / cells_per_row + 1) * cell_height)
+		if (y < BORDER_SIZE) 
 			return -1;
-
-		int column = (int) ((x - BORDER_SIZE) / cell_width);
-		int row = (int) ((y - BORDER_SIZE) / cell_height);
-		int cell_num = column + row * cells_per_row;
-
-		if (cell_num < query.Photos.Length)
-			return (int) cell_num;
-		else
+		int found_y = BORDER_SIZE;
+		bool found = false;
+		int cell_num = 0;  
+		
+		foreach (ImportPathInfo info in import_path_info) {
+			if (y > found_y && y <= found_y + path_height)
+				break;
+			found_y += path_height;
+			int num_photos = info.Count;
+			int rows = num_photos / cells_per_row; 
+			int cols = num_photos % cells_per_row;
+			for (int i = 0; i < rows; i++) {
+				if (y > found_y 
+					&& y <= found_y + cell_height) {
+					cell_num += x / cell_width;
+					found = true;
+					break;
+				}	
+				cell_num += cells_per_row;			
+				found_y += cell_height;
+			} 
+			if (found) break;
+			if (cols > 0) {
+				if (y > found_y 
+					&& y <= found_y + cell_height 
+					&& x <= cols * cell_width) {
+					cell_num += x / cell_width;
+					found = true;
+					break;
+				}	
+				cell_num += cols;
+				found_y += cell_height;
+			}
+			found_y += BORDER_END_IMPORT_PATH;	
+		}	
+		if (found == false) 
 			return -1;
+		return cell_num;
+		
 	}
 
 	public void UnselectAllCells ()
@@ -236,6 +303,32 @@
 			SelectionChanged (this);
 	}
 
+	private void UpdateImportPathInfo ()
+	{
+		import_path_info.Clear ();
+		if (query.Photos.Length <= 0) return;
+		ImportPathInfo info = new ImportPathInfo ();
+		Photo photo = query.Photos [0];
+		string photo_path = photo.DirectoryPath;
+		int count = 0;
+		for (int i = 0; i < query.Photos.Length; i++) {
+			photo = query.Photos [i];
+			if (photo_path != photo.DirectoryPath) {
+				info.Count = count;
+				info.Pathname = photo_path;
+				import_path_info.Add (info);
+				photo_path = photo.DirectoryPath;
+				count = 1;
+			} else {
+				count ++;
+			}
+		}
+		info.Count = count;
+		info.Pathname = photo_path;
+		import_path_info.Add (info);
+		return;
+	}
+
 	private bool CellIsSelected (int cell_num)
 	{
 		return selected_cells.ContainsKey (cell_num);
@@ -305,10 +398,35 @@
 
 	// Layout and drawing.
 
+	int GetIconViewHeight ()
+	{
+		int height = 0;
+		foreach (ImportPathInfo info in import_path_info) {
+			height += path_height;
+			int photo_num = 0;
+			int num_photos = info.Count;
+			while (photo_num < num_photos) {
+				photo_num += cells_per_row;
+				height += cell_height;	
+			}
+			height += BORDER_END_IMPORT_PATH; 
+		}
+
+		return height;
+	}
+	
 	private void UpdateLayout ()
 	{
-		int available_width = Allocation.Width - 2 * BORDER_SIZE;
+		int available_width = Allocation.Width - 3 * BORDER_SIZE;
+	
+		Pango.Layout layout = new Pango.Layout (PangoContext);
+		layout.SetMarkup ("Hello, world");
+		int p_x, p_y;
+		layout.GetPixelSize (out p_x, out p_y);
 
+		path_height = BORDER_ABOVE_PATHNAME 
+			+ BORDER_BELOW_PATHNAME + p_y;	
+	
 		cell_width = ThumbnailWidth + 2 * CELL_BORDER_WIDTH;
 		cell_height = ThumbnailHeight + 2 * CELL_BORDER_WIDTH;
 
@@ -318,20 +436,10 @@
 		cells_per_row = (int) (available_width / cell_width);
 		if (cells_per_row == 0)
 			cells_per_row = 1;
-
-		int num_thumbnails;
-		if (query != null)
-			num_thumbnails = query.Photos.Length;
-		else
-			num_thumbnails = 0;
-
-		int num_rows = num_thumbnails / cells_per_row;
-		if (num_thumbnails % cells_per_row != 0)
-			num_rows ++;
-
-		SetSize ((uint) Allocation.Width, (uint) (num_rows * cell_height + 2 * BORDER_SIZE));
+	
+		SetSize ((uint) Allocation.Width, (uint) (GetIconViewHeight () + 2 * BORDER_SIZE));
 	}
-
+	
 	// FIXME Cache the GCs?
 	private void DrawCell (int thumbnail_num, int x, int y)
 	{
@@ -427,45 +535,341 @@
 		}
 	}
 
-	private void DrawAllCells (int x, int y, int width, int height)
+	private void DrawPath (int index, int y) 
 	{
-		int start_cell_column = Math.Max ((x - BORDER_SIZE) / cell_width, 0);
-		int start_cell_row = Math.Max ((y - BORDER_SIZE) / cell_height, 0);
-		int start_cell_num = start_cell_column + start_cell_row * cells_per_row;
-
-		int start_cell_x, cell_y;
-		GetCellPosition (start_cell_num, out start_cell_x, out cell_y);
+		int width = cells_per_row * cell_width + 2 * BORDER_SIZE;	
+		Pango.Layout layout = new Pango.Layout (PangoContext);
+		ImportPathInfo info = (ImportPathInfo) import_path_info [index];
+		string photo_path = info.Pathname;
+		layout.SetMarkup (photo_path);
+		BinWindow.DrawRectangle (
+			Style.BackgroundGC (StateType.Insensitive),
+			true, 0 + BORDER_SIZE, 
+			y + BORDER_ABOVE_PATHNAME/2, 
+			width, path_height - BORDER_ABOVE_PATHNAME/2); 
+		BinWindow.DrawLayout (Style.TextGC (StateType.Insensitive),
+			0 + 2 * BORDER_SIZE, y + BORDER_ABOVE_PATHNAME, layout);
+		BinWindow.DrawLine (Style.ForegroundGC (StateType.Insensitive),
+			0 + BORDER_SIZE, y + BORDER_ABOVE_PATHNAME/2, 
+			0 + BORDER_SIZE, y + path_height);	
+		BinWindow.DrawLine (Style.ForegroundGC (StateType.Insensitive),
+			0 + BORDER_SIZE, y + BORDER_ABOVE_PATHNAME/2, 
+			0 + BORDER_SIZE + width, y + BORDER_ABOVE_PATHNAME/2);	
+		BinWindow.DrawLine (Style.ForegroundGC (StateType.Insensitive),
+			0 + BORDER_SIZE + width, y + BORDER_ABOVE_PATHNAME/2, 
+			0 + BORDER_SIZE + width, y + path_height);	
+	}
+		
+	private void DrawBackground (int y)
+	{
+		int width = cells_per_row * cell_width + 2 * BORDER_SIZE;
+		BinWindow.DrawRectangle (
+			Style.BackgroundGC (StateType.Insensitive),
+			true, 0 + BORDER_SIZE, 
+			y, width, cell_height);
+		BinWindow.DrawLine (
+			Style.ForegroundGC (StateType.Insensitive),
+			0 + BORDER_SIZE, y, 
+			0 + BORDER_SIZE, y + cell_height);
+		BinWindow.DrawLine (
+			Style.ForegroundGC (StateType.Insensitive),
+			0 + BORDER_SIZE + width, y, 
+			0 + BORDER_SIZE + width, y + cell_height);
+	}
+
+	private void DrawEndPath (int y)
+	{
+		int width = cells_per_row * cell_width + 2 * BORDER_SIZE;
+		BinWindow.DrawRectangle (
+			Style.BackgroundGC (StateType.Insensitive), 
+			true, 0 + BORDER_SIZE, 
+			y, width, BORDER_END_IMPORT_PATH);
+		BinWindow.DrawLine (
+			Style.ForegroundGC (StateType.Insensitive),
+			0 + BORDER_SIZE, y,
+			0 + BORDER_SIZE, y + BORDER_END_IMPORT_PATH);
+		BinWindow.DrawLine (
+			Style.ForegroundGC (StateType.Insensitive),
+			0 + BORDER_SIZE, y + BORDER_END_IMPORT_PATH,
+			0 + BORDER_SIZE + width, y + BORDER_END_IMPORT_PATH);
+		BinWindow.DrawLine (
+			Style.ForegroundGC (StateType.Insensitive),
+			0 + BORDER_SIZE + width, y,
+			0 + BORDER_SIZE + width, y + BORDER_END_IMPORT_PATH);
+	}
+
+	private int GetPhotoNumAbsolute (int index, int photo_num)
+	{
+		int return_photo_num = 0;
+		for (int i = 0; i < index; i++) {
+			ImportPathInfo info = 
+				(ImportPathInfo) import_path_info [i];
+			return_photo_num += info.Count;
+		}	
+
+		return_photo_num += photo_num;
+		return return_photo_num;
+	}
+
+	private int GetNumPhotos (int index)
+	{
+		ImportPathInfo info = (ImportPathInfo) import_path_info [index];
+		return info.Count;
+	}
+
+	private void GetStartPhoto (int y, 
+			out int start_y, 
+			out int start_photo_path,
+			out int start_photo, 
+			out int start_on_path
+			)
+	{
+		start_y = BORDER_SIZE;
+		start_photo_path = -1;
+		int photo_num = 0;
+		start_on_path = 0;
+		foreach (ImportPathInfo info in import_path_info) {
+			start_photo_path++;
+			int num_photos = info.Count;
+			photo_num = 0;
+			if (start_y + path_height >= y) {
+				start_on_path = 1;
+				break;
+			} else {
+				start_y += path_height;
+			}
+			bool found = false;
+			while (!found) {
+				if (start_y + cell_height > y) {
+					found = true;
+					break;
+				}
+				start_y += cell_height;
+				photo_num += cells_per_row;
+				if (photo_num >= num_photos) {
+					// start_photo_path++;
+					break;
+				}
+			}
+			if (found) break;
+			start_y += BORDER_END_IMPORT_PATH; 
+		}
+		start_photo = photo_num;
+		return;
+	}
 
-		int end_cell_column = Math.Max ((x + width - BORDER_SIZE) / cell_width, 0);
-		int end_cell_row = Math.Max ((y + height - BORDER_SIZE) / cell_height, 0);
+	private void GetEndPhoto (int y, 
+			out int end_y, 
+			out int end_photo_path,
+			out int end_photo,
+			out int end_on_path)
+	{
+		int photo_num = 0;
+		end_photo_path = -1;
+		end_photo = 0;
+		end_on_path = 0;
+		end_y = BORDER_SIZE;
+		foreach (ImportPathInfo info in import_path_info) {
+			end_photo_path++;
+			int num_photos = info.Count;
+			photo_num = 0;
+			if (end_y + path_height > y) {
+				end_on_path = 1;
+				break;		
+			} else {
+				end_y += path_height;
+			}	
+			bool found = false;
+			while (!found) {
+				if (end_y + cell_height > y) {
+					photo_num += cells_per_row;
+					if (photo_num > num_photos) 
+						photo_num = num_photos;
+					found = true;
+					break;
+				}
+				photo_num += cells_per_row;
+				end_y += cell_height; 
+				if (photo_num >= num_photos) {
+					photo_num = num_photos;
+					break;	
+				}
+			}
+			if (found) break;
+			end_y += BORDER_END_IMPORT_PATH;
+		}
+		end_photo = photo_num - 1;
+		return;
+	}
 
-		int num_rows = end_cell_row - start_cell_row + 1;
-		int num_cols = Math.Min (end_cell_column - start_cell_column + 1,
-					 cells_per_row - start_cell_column);
+	private void GetPhotoNumIndexed (int photo_num, out int index, out int num)
+	{
+		index = 0;
+		num = 0;
+		int curr_photo = 0;
+		foreach (ImportPathInfo info in import_path_info) 
+		{
+			int num_photos = info.Count;
+			if (photo_num < curr_photo + num_photos) {
+				num = photo_num - curr_photo; 
+				return;
+			}	
+			else {
+				index ++;
+				curr_photo += num_photos;
+			}
+		} 
+	}
 
-		int i, cell_num;
-		for (i = 0, cell_num = start_cell_num;
-		     i < num_rows && cell_num < query.Photos.Length;
-		     i ++) {
-			int cell_x = start_cell_x;
+	private void DrawAllCells (int x, int y, int width, int height)
+	{
+		
+		// x, y, x + width, y + height -> width of exposure ..
+		int start_y = BORDER_SIZE, end_y = BORDER_SIZE;
+		int start_photo = 0, end_photo = 0;
+		int photo_num = 0;
+		int start_on_path = 0, end_on_path = 0;
+		DictionaryEntry sp, ep;
+		sp = new DictionaryEntry ();
+		ep = new DictionaryEntry ();
+		int start_photo_path = 0, end_photo_path = 0;
+
+		if (query.Photos.Length <= 0) return;
+		GetStartPhoto (y, out start_y, out start_photo_path, out start_photo, out start_on_path);   
+		GetEndPhoto (y + height, out end_y, out end_photo_path, out end_photo, out end_on_path);
+	
+		int n_photos = 0;
+		bool draw_end_path = false;
 
-			for (int j = 0; j < num_cols && cell_num + j < query.Photos.Length; j ++) {
-				DrawCell (cell_num + j, cell_x, cell_y);
-				cell_x += cell_width;
+		// start drawing the photos ...
+		if (start_on_path  == 1) {
+			DrawPath (start_photo_path, start_y);
+			start_y += path_height;
+		} 
+		if (start_photo_path == end_photo_path) { 	
+			n_photos = end_photo - start_photo + 1;
+			int num_p = GetNumPhotos (start_photo_path);
+			if (end_photo + 1 == num_p) {
+				draw_end_path = true;
+			} 
+		} else {
+			n_photos = GetNumPhotos (start_photo_path) 
+				- start_photo;
+			draw_end_path = true;
+		}
+		for (int j = 0; j < n_photos;) {
+			DrawBackground (start_y); 
+			for (int k = 0; k < cells_per_row 
+				&& j + k < n_photos;
+				k++) {
+				photo_num = GetPhotoNumAbsolute (
+						start_photo_path, 
+						start_photo + j + k);
+				DrawCell (photo_num, 
+					BORDER_SIZE * 2 + cell_width * k, 
+					start_y);
 			}
-
-			cell_y += cell_height;
-			cell_num += cells_per_row;
+			j += cells_per_row;
+			start_y += cell_height; 
+		}
+		if (draw_end_path) {
+			DrawEndPath (start_y);
+			start_y += BORDER_END_IMPORT_PATH;
+		}
+		// draw rest of the photos ...
+		for (int i = start_photo_path + 1; i < end_photo_path; i++) {
+			// render path
+			DrawPath (i, start_y);
+			start_y += path_height;
+			// draw all photos in this path ...
+			int num_photos = GetNumPhotos (i);
+			int rows = num_photos / cells_per_row;
+			int cols = num_photos % cells_per_row;
+			for (int k = 0; k < rows; k ++) {
+				DrawBackground (start_y);
+				for (int m = 0; m < cells_per_row; m++) {
+					int index = GetPhotoNumAbsolute (i, 
+						k * cells_per_row + m);
+					DrawCell (index,
+						BORDER_SIZE * 2 + cell_width * m,
+						start_y);
+				}
+				start_y += cell_height;
+			}
+			if (cols > 0) {
+				DrawBackground (start_y);
+				for (int n = 0; n < cols; n++) {
+					int index = GetPhotoNumAbsolute (i, 
+						rows * cells_per_row + n);
+					DrawCell (index, 
+						BORDER_SIZE * 2 + cell_width * n, 	
+						start_y);
+				}
+				start_y += cell_height;
+			}
+			DrawEndPath (start_y);
+			start_y += BORDER_END_IMPORT_PATH; 
+		}
+		// draw last ... 
+		if (start_photo_path != end_photo_path) {
+			// not drawn already ...
+			if (end_on_path == 1) {
+				DrawPath (end_photo_path, start_y);
+				start_y += path_height;
+			} else {
+				DrawPath (end_photo_path, start_y);
+				start_y += path_height;
+				n_photos = end_photo;
+				for (int j = 0; j <= n_photos;) {
+					DrawBackground (start_y);
+					for (int k = 0; k < cells_per_row
+						&& j + k <= n_photos; k++) {
+						photo_num = GetPhotoNumAbsolute (
+							end_photo_path,
+							j + k);
+						DrawCell (photo_num,
+							BORDER_SIZE * 2 + cell_width * k,
+							start_y);
+							
+					}
+					j += cells_per_row;
+					start_y += cell_height;
+				}
+				int num_p = GetNumPhotos (end_photo_path);
+				if (end_photo + 1 == num_p) {
+					DrawEndPath (start_y);
+					start_y += BORDER_END_IMPORT_PATH;
+				}	 	
+			}	
 		}
 	}
 
 	private void GetCellPosition (int cell_num, out int x, out int y)
 	{
-		int row = cell_num / cells_per_row;
-		int col = cell_num % cells_per_row;
+		int photo_num = 0;
+		int curr_photo = 0;
+		x = 0; 
+		y = BORDER_SIZE;
+		int n_photos = 0;
+		foreach (ImportPathInfo info in import_path_info) {
+			n_photos = info.Count;
+			y += path_height;
+			if (curr_photo + n_photos > cell_num) {
+				break;
+			}
+			curr_photo += n_photos;
+			int r = n_photos / cells_per_row;
+			y += r * cell_height;
+			if (n_photos % cells_per_row != 0) {
+				y += cell_height;
+			}
+			y += BORDER_END_IMPORT_PATH;	
+		}
+		int row = (cell_num - curr_photo) / cells_per_row;
+		int col = (cell_num - curr_photo) % cells_per_row;
 
-		x = col * cell_width + BORDER_SIZE;
-		y = row * cell_height + BORDER_SIZE;
+		x = col * cell_width + BORDER_SIZE * 2;
+		y += row * cell_height; 
 	}
 
 
@@ -477,6 +881,7 @@
 
 	private bool HandleScrollOnIdle ()
 	{
+		/*
 		Adjustment adjustment = Vadjustment;
 
 		if (y_offset == adjustment.Value)
@@ -511,6 +916,7 @@
 		y_offset = (int) adjustment.Value;
 
 		scroll_on_idle_id = 0;
+		*/
 		return false;
 	}
 
@@ -593,6 +999,7 @@
 
 		if (cell_num < 0) {
 			args.RetVal = false;
+			UnselectAllCells ();
 			return;
 		}
 
@@ -609,7 +1016,7 @@
 		case EventType.ButtonPress:
 			if (args.Event.Button != 1)
 				return;
-
+			
 			GrabFocus ();
 			if ((args.Event.State & ModifierType.ControlMask) != 0) {
 				ToggleCell (cell_num);
@@ -619,16 +1026,14 @@
 				UnselectAllCells ();
 				SelectCell (cell_num);
 			}
-
 			Gdk.Pointer.Grab (BinWindow, false,
 					  EventMask.ButtonReleaseMask | EventMask.Button1MotionMask,
 					  null, null, args.Event.Time);
-
 			click_x = (int) args.Event.X;
 			click_y = (int) args.Event.Y;
-
+			
 			focus_cell = cell_num;
-
+			
 			return;
 
 		default:
@@ -664,9 +1069,22 @@
 		
 
 		focus_old = focus_cell;
+		int photo_index, photo_num, num_photos;
 		switch (args.Event.Key) {
 			case Gdk.Key.Down:
-				focus_cell += cells_per_row;
+				GetPhotoNumIndexed (focus_cell, 
+						out photo_index, 
+						out photo_num);
+				num_photos = GetNumPhotos (photo_index);
+				if (photo_num == num_photos - 1) {
+					focus_cell ++;
+				} else if (photo_num + cells_per_row >= num_photos) {
+					focus_cell = GetPhotoNumAbsolute (photo_index,
+						num_photos - 1);
+					break;
+				} else {
+					focus_cell += cells_per_row;
+				}  
 				break;
 			case Gdk.Key.Left:
 				focus_cell--;
@@ -675,7 +1093,17 @@
 				focus_cell++;
 				break;
 			case Gdk.Key.Up:
-				focus_cell -= cells_per_row;
+				GetPhotoNumIndexed (focus_cell, 
+					out photo_index, 
+					out photo_num);
+				num_photos = GetNumPhotos (photo_index);
+				if (photo_num == 0) { 
+					focus_cell --;
+				} else if (photo_num - cells_per_row < 0) {
+					focus_cell -= (photo_num + 1);
+				} else {
+					focus_cell -= cells_per_row;
+				}
 				break;
 			case Gdk.Key.Home:
 				focus_cell = 0;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]