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 | 	[_] 62% High priority | ||||||
| 		[_] BUG: sorting breaks when at or near the end of a ribbon... | 		[_] BUG: sorting breaks when at or near the end of a ribbon... | ||||||
| 		| | 		| | ||||||
|  | 		| Race condition... | ||||||
|  | 		| | ||||||
| 		| Procedure: | 		| Procedure: | ||||||
| 		| 	- go to end of a ribbon | 		| 	- go to end of a ribbon | ||||||
| 		| 	- shift-s  | 		| 	- shift-s  | ||||||
| @ -125,7 +127,28 @@ Roadmap | |||||||
| 		| 			- check "Descending" in the sort dialog | 		| 			- check "Descending" in the sort dialog | ||||||
| 		| NOTE: this breaks because current the current image is not  | 		| NOTE: this breaks because current the current image is not  | ||||||
| 		| 		yet loaded/created when reloadViewer(..) tries to focus it... | 		| 		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... | 		[_] 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... | 		[_] buildcache: add option to control image sort... | ||||||
| 		[_] ASAP: Need visual indicators for long operations... | 		[_] ASAP: Need visual indicators for long operations... | ||||||
| 		[_] 66% tags | 		[_] 66% tags | ||||||
| @ -269,17 +292,6 @@ Roadmap | |||||||
| 			[_] 0% metadata | 			[_] 0% metadata | ||||||
| 				[_] comment | 				[_] comment | ||||||
| 				[_] tags | 				[_] 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 | 		[_] BUG: opening a dir form history sometimes loads wrong size previews | ||||||
| 		| this happens in part of the view and a refresh, reload or image | 		| this happens in part of the view and a refresh, reload or image | ||||||
| 		| update (updateImages()) fixes the issue... | 		| update (updateImages()) fixes the issue... | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ | |||||||
| // list of bookmarked gids...
 | // list of bookmarked gids...
 | ||||||
| //
 | //
 | ||||||
| // NOTE: this must be sorted in the same order as DATA.order
 | // NOTE: this must be sorted in the same order as DATA.order
 | ||||||
| var BOOKMARKS= []  | var BOOKMARKS = []  | ||||||
| 
 | 
 | ||||||
| // bookmark data
 | // bookmark data
 | ||||||
| //
 | //
 | ||||||
| @ -24,6 +24,9 @@ var BOOKMARKS_FILE_PATTERN = /^[0-9]*-bookmarked.json$/ | |||||||
| * Helpers | * Helpers | ||||||
| */ | */ | ||||||
| 
 | 
 | ||||||
|  | var getBookmarked = makeMarkedLister(function(){ return BOOKMARKS }) | ||||||
|  | var getUnbookmarked = makeUnmarkedLister(function(){ return BOOKMARKS })  | ||||||
|  | 
 | ||||||
| var getBookmarkedGIDBefore = makeGIDBeforeGetterFromList( | var getBookmarkedGIDBefore = makeGIDBeforeGetterFromList( | ||||||
| 		function(){  | 		function(){  | ||||||
| 			return BOOKMARKS  | 			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 :                                                */ | * 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...
 | // Get all the currently loaded gids...
 | ||||||
| //
 | //
 | ||||||
| // NOTE: this will return an unsorted list of gids...
 | // NOTE: this will return an unsorted list of gids...
 | ||||||
| function getLoadedGIDs(data){ | function getLoadedGIDs(gids, data){ | ||||||
| 	data = data == null ? DATA : data | 	data = data == null ? DATA : data | ||||||
| 	var res = [] | 	var res = [] | ||||||
| 	data.ribbons.forEach(function(r){ | 	data.ribbons.forEach(function(r){ | ||||||
| 		res = res.concat(r) | 		res = res.concat(r) | ||||||
| 	}) | 	}) | ||||||
|  | 	if(gids != null){ | ||||||
|  | 		return gids.filter(function(e){ | ||||||
|  | 			return res.indexOf(e) >= 0 | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
| 	return res | 	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 | * 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 
 | 		// NOTE: this is handled by the wrapper at this point, so we do 
 | ||||||
| 		// 		not have to do anything here...
 | 		// 		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: { | 		F: { | ||||||
| 			ctrl: 'F11', | 			ctrl: 'F11', | ||||||
| 		}, | 		}, | ||||||
| @ -380,6 +383,7 @@ var KEYBOARD_CONFIG = { | |||||||
| 
 | 
 | ||||||
| 		// cropping...
 | 		// cropping...
 | ||||||
| 		C: doc('Show ribbon crop dialog', cropImagesDialog), | 		C: doc('Show ribbon crop dialog', cropImagesDialog), | ||||||
|  | 		F: doc('Filter images', filterImagesDialog), | ||||||
| 
 | 
 | ||||||
| 		// XXX add a non FXX key for macs...
 | 		// XXX add a non FXX key for macs...
 | ||||||
| 		F2: { | 		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:
 | // mode can be:
 | ||||||
| // 	- 'ribbon'
 | // 	- 'ribbon'
 | ||||||
| // 	- 'all'
 | // 	- 'all'
 | ||||||
| // 	- number			- ribbon index
 | // 	- number			- ribbon index
 | ||||||
|  | // 	- list				- list of gids used as source
 | ||||||
| // 	- null				- same as all
 | // 	- null				- same as all
 | ||||||
| function getUnmarked(mode){ | function makeUnmarkedLister(get_marked, get_cache){ | ||||||
| 	mode = mode == null ? 'all' : mode | 	return function(mode){ | ||||||
| 	var gids = mode == 'all' ? getLoadedGIDs()  | 		var marked = get_marked() | ||||||
| 		: typeof(mode) == typeof(123) ? getRibbonGIDs(mode) | 		var cache = get_cache != null ? get_cache() : null | ||||||
| 		: getRibbonGIDs() |  | ||||||
| 	mode = mode == 'ribbon' ? getRibbonIndex() : mode |  | ||||||
| 
 | 
 | ||||||
| 	// get the cached set...
 | 		mode = mode == null ? 'all' : mode | ||||||
| 	if(_UNMARKED_CACHE != null && mode in _UNMARKED_CACHE){ | 		var gids = mode == 'all' ? getLoadedGIDs()  | ||||||
| 		return _UNMARKED_CACHE[mode] | 			: 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( | var getMarkedGIDBefore = makeGIDBeforeGetterFromList( | ||||||
| 		function(){  | 		function(){  | ||||||
| 			return MARKED  | 			return MARKED  | ||||||
|  | |||||||
| @ -418,9 +418,9 @@ function sortImagesDialog(){ | |||||||
| 
 | 
 | ||||||
| 	cfg = {} | 	cfg = {} | ||||||
| 	cfg[alg] = [ | 	cfg[alg] = [ | ||||||
| 		'Date |'+ | 		'Date |' | ||||||
| 			'fall back to file sequence then\n'+ | 			+'fall back to file sequence then\n' | ||||||
| 			'file name when the earlier is equal.',  | 			+'file name when the earlier is equal.',  | ||||||
| 		'Sequence number',  | 		'Sequence number',  | ||||||
| 		'Sequence number with overflow',  | 		'Sequence number with overflow',  | ||||||
| 		'File name'  | 		'File name'  | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								ui/ui.js
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								ui/ui.js
									
									
									
									
									
								
							| @ -808,7 +808,7 @@ var FIELD_TYPES = { | |||||||
| 			var select = field.find('select') | 			var select = field.find('select') | ||||||
| 			for(var i=0; i < value.select.length; i++){ | 			for(var i=0; i < value.select.length; i++){ | ||||||
| 				item | 				item | ||||||
| 					.text(value.select[i]) | 					.html(value.select[i]) | ||||||
| 					.val(value.select[i]) | 					.val(value.select[i]) | ||||||
| 				item.appendTo(select) | 				item.appendTo(select) | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user