mirror of
https://github.com/flynx/ImageGrid.git
synced 2025-10-29 18:30:09 +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