mirror of
				https://github.com/flynx/ImageGrid.git
				synced 2025-10-31 03:10:07 +00:00 
			
		
		
		
	added a filter dialog...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
		
							parent
							
								
									71141dd571
								
							
						
					
					
						commit
						d2cff759ac
					
				
							
								
								
									
										34
									
								
								ui/TODO.otl
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								ui/TODO.otl
									
									
									
									
									
								
							| @ -112,6 +112,8 @@ Roadmap | ||||
| 	[_] 62% High priority | ||||
| 		[_] BUG: sorting breaks when at or near the end of a ribbon... | ||||
| 		| | ||||
| 		| Race condition... | ||||
| 		| | ||||
| 		| Procedure: | ||||
| 		| 	- go to end of a ribbon | ||||
| 		| 	- shift-s  | ||||
| @ -125,7 +127,28 @@ Roadmap | ||||
| 		| 			- check "Descending" in the sort dialog | ||||
| 		| NOTE: this breaks because current the current image is not  | ||||
| 		| 		yet loaded/created when reloadViewer(..) tries to focus it... | ||||
| 		| | ||||
| 		| Temporary workaround: | ||||
| 		| 	because there is nothing wrong with sorting itself, just the | ||||
| 		| 	UI, the resulting state can be fixed by simply reloading the  | ||||
| 		| 	viewer (reloadViewer(true) or ctrl-alt-r) | ||||
| 		[_] BUG: sorting mis-aligns ribbons in some cases... | ||||
| 		| Example: | ||||
| 		| 				 oooo...	--[reverse]->	   ...oooo | ||||
| 		| 		...oooo[o]oooo...					...oooo[o]oooo... | ||||
| 		| | ||||
| 		| 		Should be: | ||||
| 		| 				 oooo...	--[reverse]->	 ...oooo | ||||
| 		| 		...oooo[o]oooo...					...oooo[o]oooo... | ||||
| 		| | ||||
| 		| 	The above can happen when, for example, sorting the images via data | ||||
| 		| 	and then sorting them in the same way with reverse checked... | ||||
| 		| | ||||
| 		| XXX is this related to? | ||||
| 		|	BUG: sorting breaks when at or near the end of a ribbon... | ||||
| 		[_] BUG: panels: open/close events get triggered on panel drag/sort... | ||||
| 		[_] crop/filter/search dialog... | ||||
| 		| make a number of fields each accepting a filter -- string/regexp | ||||
| 		[_] buildcache: add option to control image sort... | ||||
| 		[_] ASAP: Need visual indicators for long operations... | ||||
| 		[_] 66% tags | ||||
| @ -269,17 +292,6 @@ Roadmap | ||||
| 			[_] 0% metadata | ||||
| 				[_] comment | ||||
| 				[_] tags | ||||
| 		[_] BUG: sorting mis-aligns ribbons in some cases... | ||||
| 		| Example: | ||||
| 		| 				 oooo...	--[reverse]->	   ...oooo | ||||
| 		| 		...oooo[o]oooo...					...oooo[o]oooo... | ||||
| 		| | ||||
| 		| 		Should be: | ||||
| 		| 				 oooo...	--[reverse]->	 ...oooo | ||||
| 		| 		...oooo[o]oooo...					...oooo[o]oooo... | ||||
| 		| | ||||
| 		| 	The above can happen when, for example, sorting the images via data | ||||
| 		| 	and then sorting them in the same way with reverse checked... | ||||
| 		[_] BUG: opening a dir form history sometimes loads wrong size previews | ||||
| 		| this happens in part of the view and a refresh, reload or image | ||||
| 		| update (updateImages()) fixes the issue... | ||||
|  | ||||
| @ -7,7 +7,7 @@ | ||||
| // list of bookmarked gids...
 | ||||
| //
 | ||||
| // NOTE: this must be sorted in the same order as DATA.order
 | ||||
| var BOOKMARKS= []  | ||||
| var BOOKMARKS = []  | ||||
| 
 | ||||
| // bookmark data
 | ||||
| //
 | ||||
| @ -24,6 +24,9 @@ var BOOKMARKS_FILE_PATTERN = /^[0-9]*-bookmarked.json$/ | ||||
| * Helpers | ||||
| */ | ||||
| 
 | ||||
| var getBookmarked = makeMarkedLister(function(){ return BOOKMARKS }) | ||||
| var getUnbookmarked = makeUnmarkedLister(function(){ return BOOKMARKS })  | ||||
| 
 | ||||
| var getBookmarkedGIDBefore = makeGIDBeforeGetterFromList( | ||||
| 		function(){  | ||||
| 			return BOOKMARKS  | ||||
|  | ||||
							
								
								
									
										129
									
								
								ui/crop.js
									
									
									
									
									
								
							
							
						
						
									
										129
									
								
								ui/crop.js
									
									
									
									
									
								
							| @ -273,6 +273,135 @@ function cropImagesDialog(){ | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| function filterImagesDialog(){ | ||||
| 	updateStatus('Filter...').show() | ||||
| 
 | ||||
| 	cfg = {} | ||||
| 	cfg['sep0'] = '---' | ||||
| 	cfg['Name'] = '' | ||||
| 	cfg['Path |' | ||||
| 			+'this applies to the non-common\n' | ||||
| 			+'part of the relative path.'] = '' | ||||
| 	cfg['Comment'] = '' | ||||
| 	cfg['Tags |' | ||||
| 			+'an image will match if at least\n' | ||||
| 			+'one tag matches'] = '' | ||||
| 	// XXX date...
 | ||||
| 	cfg['Rotated'] = {select: [ | ||||
| 		'', | ||||
| 		'no', | ||||
| 		'90° or 270°', | ||||
| 		'0° or 180°', | ||||
| 		'90° only', | ||||
| 		'180° only', | ||||
| 		'270° only' | ||||
| 	]} | ||||
| 	cfg['Flipped'] = {select: [ | ||||
| 		'', | ||||
| 		'no', | ||||
| 		'vertical', | ||||
| 		'horizontal' | ||||
| 	]} | ||||
| 	cfg['sep1'] = '---' | ||||
| 	cfg['Marked'] = {select: [ | ||||
| 		'', | ||||
| 		'yes', | ||||
| 		'no' | ||||
| 	]} | ||||
| 	cfg['Bookmarked'] = {select: [ | ||||
| 		'', | ||||
| 		'yes', | ||||
| 		'no' | ||||
| 	]} | ||||
| 	cfg['sep2'] = '---' | ||||
| 	cfg['Keep ribbons'] = false | ||||
| 
 | ||||
| 	formDialog(null,  | ||||
| 			'Filter images | NOTE: all filter text fields\n' | ||||
| 							+'support regular expressions.', | ||||
| 			cfg, | ||||
| 			'OK',  | ||||
| 			'filterImagesDialog') | ||||
| 		.done(function(res){ | ||||
| 			var gids | ||||
| 
 | ||||
| 			showStatusQ('Filtering...') | ||||
| 
 | ||||
| 			// XXX date...
 | ||||
| 
 | ||||
| 			var filter = {} | ||||
| 			// build the filter...
 | ||||
| 			for(var field in res){ | ||||
| 				if(/^Name/.test(field) && res[field].trim() != ''){ | ||||
| 					filter['name'] = res[field] | ||||
| 
 | ||||
| 				} else if(/^Path/.test(field) && res[field].trim() != ''){ | ||||
| 					filter['path'] = res[field] | ||||
| 
 | ||||
| 				} else if(/^Comment/.test(field) && res[field].trim() != ''){ | ||||
| 					filter['comment'] = res[field] | ||||
| 
 | ||||
| 				} else if(/^Tags/.test(field) && res[field].trim() != ''){ | ||||
| 					filter['tags'] = res[field] | ||||
| 
 | ||||
| 				} else if(/^Rotated/.test(field) && res[field].trim() != ''){ | ||||
| 					if(res[field] == 'no'){ | ||||
| 						filter['orientation'] = '^0$|undefined|null' | ||||
| 					} else if(/or/.test(res[field])){ | ||||
| 						filter['orientation'] = res[field] | ||||
| 							.split('or') | ||||
| 							.map(function(e){ | ||||
| 								e = parseInt(e) | ||||
| 								if(e == 0){ | ||||
| 									return '^0$|undefined|null' | ||||
| 								} | ||||
| 								return e | ||||
| 							}) | ||||
| 							.join('|') | ||||
| 					} else { | ||||
| 						filter['orientation'] = RegExp(parseInt(res[field])) | ||||
| 					} | ||||
| 
 | ||||
| 				} else if(/^Flipped/.test(field) && res[field].trim() != ''){ | ||||
| 					if(res[field] == 'no'){ | ||||
| 						filter['flipped'] = 'undefined|null' | ||||
| 					} else { | ||||
| 						filter['flipped'] = res[field] | ||||
| 					} | ||||
| 
 | ||||
| 				} else if(/^Bookmarked/.test(field) && res[field].trim() != ''){ | ||||
| 					if(res[field] == 'yes'){ | ||||
| 						gids = getBookmarked(gids) | ||||
| 					} else { | ||||
| 						gids = getUnbookmarked(gids) | ||||
| 					} | ||||
| 
 | ||||
| 				} else if(/^Marked/.test(field) && res[field].trim() != ''){ | ||||
| 					if(res[field] == 'yes'){ | ||||
| 						gids = getMarked(gids) | ||||
| 					} else { | ||||
| 						gids = getUnmarked(gids) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			var keep_ribbons = res['Keep ribbons'] | ||||
| 
 | ||||
| 			gids = filterGIDs(filter, gids) | ||||
| 
 | ||||
| 			if(gids.length > 0){ | ||||
| 				cropDataTo(gids, keep_ribbons) | ||||
| 			} else { | ||||
| 				showStatusQ('Filter: nothing matched.') | ||||
| 			} | ||||
| 		}) | ||||
| 		.fail(function(){ | ||||
| 			showStatusQ('Filter: canceled.') | ||||
| 		}) | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /********************************************************************** | ||||
| * vim:set ts=4 sw=4 :                                                */ | ||||
|  | ||||
							
								
								
									
										48
									
								
								ui/data.js
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								ui/data.js
									
									
									
									
									
								
							| @ -748,12 +748,17 @@ function getAllGids(data){ | ||||
| // Get all the currently loaded gids...
 | ||||
| //
 | ||||
| // NOTE: this will return an unsorted list of gids...
 | ||||
| function getLoadedGIDs(data){ | ||||
| function getLoadedGIDs(gids, data){ | ||||
| 	data = data == null ? DATA : data | ||||
| 	var res = [] | ||||
| 	data.ribbons.forEach(function(r){ | ||||
| 		res = res.concat(r) | ||||
| 	}) | ||||
| 	if(gids != null){ | ||||
| 		return gids.filter(function(e){ | ||||
| 			return res.indexOf(e) >= 0 | ||||
| 		}) | ||||
| 	} | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| @ -1405,6 +1410,47 @@ function makePrevFromListAction(get_closest, get_list, restrict_to_ribbon){ | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // XXX also need a date filter -- separate function?
 | ||||
| function filterGIDs(filter, gids, data, images){ | ||||
| 	images = images == null ? IMAGES : images | ||||
| 	gids = gids == null ? getLoadedGIDs(null, data) : gids | ||||
| 
 | ||||
| 	// normalize filter...
 | ||||
| 	for(var k in filter){ | ||||
| 		if(typeof(filter[k]) == typeof('str')){ | ||||
| 			filter[k] = RegExp(filter[k]) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	var res = gids.filter(function(gid){ | ||||
| 		var img = images[gid] | ||||
| 		for(var k in filter){ | ||||
| 			// if key does not exist we have no match...
 | ||||
| 			if(!(k in img)){ | ||||
| 				return false | ||||
| 			} | ||||
| 
 | ||||
| 			var f = filter[k] | ||||
| 			var val = img[k] | ||||
| 			val = typeof(val) == typeof('str') ? val.trim() : val | ||||
| 
 | ||||
| 			// value is a list, check items, at least one needs to match...
 | ||||
| 			if(val.constructor.name == 'Array' | ||||
| 					&& val.filter(function(e){ return f.test(e) }).length < 1){ | ||||
| 				return false | ||||
| 				 | ||||
| 			// check the whole value...
 | ||||
| 			} else if(!f.test(val)){ | ||||
| 				return false | ||||
| 			} | ||||
| 		} | ||||
| 		return true | ||||
| 	}) | ||||
| 
 | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /********************************************************************** | ||||
| * Constructors and general data manipulation | ||||
|  | ||||
| @ -78,7 +78,10 @@ var KEYBOARD_CONFIG = { | ||||
| 
 | ||||
| 		// NOTE: this is handled by the wrapper at this point, so we do 
 | ||||
| 		// 		not have to do anything here...
 | ||||
| 		F11: doc('Toggle full screen view', function(){ toggleFullscreenMode() }), | ||||
| 		F11: doc('Toggle full screen view', function(){  | ||||
| 				toggleFullscreenMode()  | ||||
| 				return false | ||||
| 			}), | ||||
| 		F: { | ||||
| 			ctrl: 'F11', | ||||
| 		}, | ||||
| @ -380,6 +383,7 @@ var KEYBOARD_CONFIG = { | ||||
| 
 | ||||
| 		// cropping...
 | ||||
| 		C: doc('Show ribbon crop dialog', cropImagesDialog), | ||||
| 		F: doc('Filter images', filterImagesDialog), | ||||
| 
 | ||||
| 		// XXX add a non FXX key for macs...
 | ||||
| 		F2: { | ||||
|  | ||||
							
								
								
									
										74
									
								
								ui/marks.js
									
									
									
									
									
								
							
							
						
						
									
										74
									
								
								ui/marks.js
									
									
									
									
									
								
							| @ -74,38 +74,70 @@ function invalidateMarksCache(){ | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Get list of unmarked images...
 | ||||
| function makeMarkedLister(get_marked){ | ||||
| 	return function(mode){ | ||||
| 		var marked = get_marked() | ||||
| 		mode = mode == null ? 'all' : mode | ||||
| 		var gids = mode == 'all' ? getLoadedGIDs(marked)  | ||||
| 			: mode.constructor.name == 'Array' ? getLoadedGIDs(mode) | ||||
| 			: typeof(mode) == typeof(123) ? getRibbonGIDs(mode) | ||||
| 			: getRibbonGIDs() | ||||
| 
 | ||||
| 		if(mode == 'all'){ | ||||
| 			return gids | ||||
| 		} | ||||
| 		return gids.filter(function(e){ | ||||
| 			return marked.indexOf(e) >= 0 | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Make lister of unmarked images...
 | ||||
| //
 | ||||
| // mode can be:
 | ||||
| // 	- 'ribbon'
 | ||||
| // 	- 'all'
 | ||||
| // 	- number			- ribbon index
 | ||||
| // 	- list				- list of gids used as source
 | ||||
| // 	- null				- same as all
 | ||||
| function getUnmarked(mode){ | ||||
| 	mode = mode == null ? 'all' : mode | ||||
| 	var gids = mode == 'all' ? getLoadedGIDs()  | ||||
| 		: typeof(mode) == typeof(123) ? getRibbonGIDs(mode) | ||||
| 		: getRibbonGIDs() | ||||
| 	mode = mode == 'ribbon' ? getRibbonIndex() : mode | ||||
| function makeUnmarkedLister(get_marked, get_cache){ | ||||
| 	return function(mode){ | ||||
| 		var marked = get_marked() | ||||
| 		var cache = get_cache != null ? get_cache() : null | ||||
| 
 | ||||
| 	// get the cached set...
 | ||||
| 	if(_UNMARKED_CACHE != null && mode in _UNMARKED_CACHE){ | ||||
| 		return _UNMARKED_CACHE[mode] | ||||
| 		mode = mode == null ? 'all' : mode | ||||
| 		var gids = mode == 'all' ? getLoadedGIDs()  | ||||
| 			: mode.constructor.name == 'Array' ? getLoadedGIDs(mode) | ||||
| 			: typeof(mode) == typeof(123) ? getRibbonGIDs(mode) | ||||
| 			: getRibbonGIDs() | ||||
| 		mode = mode == 'ribbon' ? getRibbonIndex() : mode | ||||
| 
 | ||||
| 		// get the cached set...
 | ||||
| 		if(cache != null && mode in cache){ | ||||
| 			return cache[mode] | ||||
| 		} | ||||
| 
 | ||||
| 		// calculate the set...
 | ||||
| 		var res = gids.filter(function(e){ | ||||
| 			// keep only unmarked...
 | ||||
| 			return marked.indexOf(e) < 0 | ||||
| 		}) | ||||
| 		if(cache != null && typeof(mode) == typeof(123)){ | ||||
| 			cache[mode] = res | ||||
| 		} | ||||
| 
 | ||||
| 		return res | ||||
| 	} | ||||
| 
 | ||||
| 	// calculate the set...
 | ||||
| 	var res = gids.filter(function(e){ | ||||
| 		// keep only unmarked...
 | ||||
| 		return MARKED.indexOf(e) < 0 | ||||
| 	}) | ||||
| 	if(_UNMARKED_CACHE != null){ | ||||
| 		_UNMARKED_CACHE[mode] = res | ||||
| 	} | ||||
| 
 | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| var getMarked = makeMarkedLister(function(){ return MARKED }) | ||||
| var getUnmarked = makeUnmarkedLister( | ||||
| 		function(){ return MARKED },  | ||||
| 		function(){ return _UNMARKED_CACHE }) | ||||
| 
 | ||||
| 
 | ||||
| var getMarkedGIDBefore = makeGIDBeforeGetterFromList( | ||||
| 		function(){  | ||||
| 			return MARKED  | ||||
|  | ||||
| @ -418,9 +418,9 @@ function sortImagesDialog(){ | ||||
| 
 | ||||
| 	cfg = {} | ||||
| 	cfg[alg] = [ | ||||
| 		'Date |'+ | ||||
| 			'fall back to file sequence then\n'+ | ||||
| 			'file name when the earlier is equal.',  | ||||
| 		'Date |' | ||||
| 			+'fall back to file sequence then\n' | ||||
| 			+'file name when the earlier is equal.',  | ||||
| 		'Sequence number',  | ||||
| 		'Sequence number with overflow',  | ||||
| 		'File name'  | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user