diff --git a/ui/data.js b/ui/data.js index d7f876ca..a6407670 100755 --- a/ui/data.js +++ b/ui/data.js @@ -943,7 +943,7 @@ function loadFileImages(path, no_load_diffs, callback){ }).sort().reverse()[0] path = path == null ? 'images.json' : path - console.log('Loading:', path) + updateStatus('Loading: ' + path) path = base +'/'+ path @@ -972,7 +972,7 @@ function loadFileImages(path, no_load_diffs, callback){ // whether we have one or more deffereds here... .done(function(data){ diff_data[i+1] = data - console.log('Loaded:', e) + updateStatus('Loaded:', e) }) })) .then(function(){ @@ -988,12 +988,12 @@ function loadFileImages(path, no_load_diffs, callback){ $.extend(json, diff_data) IMAGES = json - console.log('Loaded IMAGES...') + updateStatus('Loaded images...') callback != null && callback() }) .fail(function(){ - console.error('ERROR LOADING:', path) + updateErrorStatus('Loading: ' + path) }) } @@ -1009,7 +1009,7 @@ function saveFileImages(name){ name = name == null ? normalizePath(CACHE_DIR +'/'+ Date.timeStamp()) : name if(window.dumpJSON == null){ - console.error('Can\'t save to file.') + updateErrorStatus('Can\'t save to file.') return } @@ -1018,7 +1018,7 @@ function saveFileImages(name){ $.each($.map(listDir(normalizePath(CACHE_DIR)), function(e){ return /.*-images-diff.json$/.test(e) ? e : null }), function(i, e){ - console.log('removeing:', e) + updateStatus('removeing:', e) removeFile(normalizePath(CACHE_DIR +'/'+ e)) }) IMAGES_UPDATED = [] @@ -1060,12 +1060,12 @@ function loadFileState(data_path, callback){ // unknown format... } else { - console.error('unknown format.') + updateStatus('Unknown format.') return } }) .fail(function(){ - console.error('ERROR LOADING:', data_path) + updateErrorStatus('Loading:', data_path) }) return res @@ -1114,10 +1114,12 @@ function loadDir(path, raw_load){ var orig_path = path var data + updateStatus('Loading...').show() + var files = listDir(path) if(files == null){ - console.error('Path error:', path) + updateErrorStatus('Path: ' + path) return } @@ -1143,11 +1145,14 @@ function loadDir(path, raw_load){ // load the found data file... if(data != null){ - console.log('Loading:', data) + updateStatus('Loading: ', data) data = path + '/' + data return loadFileState(data) + .always(function(){ + showStatus('Done.') + }) // load the dir as-is... } else { @@ -1157,7 +1162,7 @@ function loadDir(path, raw_load){ }) if(image_paths.length == 0){ - console.error('No images in:', orig_path) + updateErrorStatus('No images in:', orig_path) return } @@ -1168,6 +1173,7 @@ function loadDir(path, raw_load){ MARKED = [] loadData() + showStatus('Done.') } } @@ -1183,7 +1189,7 @@ function updateRibbonsFromFavDirs(){ // NOTE: this will open the default editor/viewer. function openImage(){ if(window.runSystem == null){ - console.error('Can\'t run external programs.') + updateErrorStatus('Can\'t run external programs.') return } // XXX if path is not present try and open the biggest preview... @@ -1193,13 +1199,61 @@ function openImage(){ /********************************************************************** -* Actions +* Info & status... */ +// NOTE: if message is null, then just return the status element... +function updateStatus(message){ + + var elem = $('.global-status') + if(elem.length == 0){ + elem = $('
') + } + if(message == null){ + return elem + } + + if(arguments.length > 1){ + message = Array.apply(Array, arguments).join(' ') + } + + if(typeof(message) == typeof('s') && /^error.*/i.test(message)){ + console.error.apply(console, arguments) + } else { + console.log.apply(console, arguments) + } + + return updateInfo(elem, message) +} +function showStatus(message){ + return updateStatus(message) + .stop() + .show() + .delay(500) + .fadeOut(800) +} +function showErrorStatus(message){ + return updateStatus('Error:' + message) + .stop() + .show() +} + + // XXX do we need a full rewrite here, or will it be better to just fill // the slots... function updateGlobalImageInfo(image){ image = image == null ? getImage() : $(image) + image = image.length == 0 ? getImage() : image + + var elem = $('.global-image-info') + if(elem.length == 0){ + elem = $('
') + } + + // no image no update... + if(image.length == 0){ + return elem + } var gid = getImageGID(image) var r = getRibbonIndex(getRibbon(image)) @@ -1222,11 +1276,6 @@ function updateGlobalImageInfo(image){ meta = meta.join(', ') meta = meta != '' ? '( '+ meta +' )' : '' - var elem = $('.global-image-info') - if(elem.length == 0){ - elem = $('
') - } - return updateInfo(elem, // path... ''+ @@ -1268,6 +1317,18 @@ function updateGlobalImageInfo(image){ function updateInlineImageInfo(image){ image = image == null ? getImage() : $(image) + image = image.length == 0 ? getImage() : image + + var elem = $('.inline-image-info') + if(elem.length == 0){ + elem = $('
') + } + + // no image no update... + if(image.length == 0){ + return elem + } + var gid = getImageGID(image) var r = getRibbonIndex(getRibbon(image)) @@ -1277,11 +1338,6 @@ function updateInlineImageInfo(image){ var orientation = data.orientation orientation = orientation == null ? 0 : orientation - var elem = $('.inline-image-info') - if(elem.length == 0){ - elem = $('
') - } - return updateInfo(elem, // name... data.path.split('/').pop() +'
'+ @@ -1297,6 +1353,16 @@ function updateInlineImageInfo(image){ } +function inlineImageInfoHoverHandler(evt){ + var img = $(evt.target).closest('.image') + if(img.length > 0){ + if(img.find('.inline-image-info:visible').length == 0){ + updateInlineImageInfo(img) + } + } +} + + /********************************************************************** * Setup diff --git a/ui/index.html b/ui/index.html index 8700b7ae..c36c4cfb 100755 --- a/ui/index.html +++ b/ui/index.html @@ -254,7 +254,8 @@ body { width: 100%; background: black; opacity: 0.7; - +} +.image .inline-image-info:hover { -moz-user-select: auto; -webkit-user-select: auto; -o-user-select: auto; @@ -328,7 +329,8 @@ body { color: white; opacity: 0.6; - +} +.overlay-info:hover { -moz-user-select: auto; -webkit-user-select: auto; -o-user-select: auto; @@ -526,6 +528,11 @@ body { display: inline; } +.global-status { + opacity: 1; + z-index: 1000; +} + @@ -578,18 +585,6 @@ $(function(){ function(k){ window.DEBUG && console.log(k) })) - .on('mouseover', function(evt){ - var img = $(evt.target).closest('.image') - if(img.length > 0){ - if(IMAGE_INFO){ - if(img.find('.inline-image-info:visible').length == 0){ - updateInlineImageInfo(img) - } - } else { - img.find('.inline-image-info').remove() - } - } - }) setupDataBindings() diff --git a/ui/keybindings.js b/ui/keybindings.js index 1f29894b..a5f922b8 100755 --- a/ui/keybindings.js +++ b/ui/keybindings.js @@ -16,7 +16,32 @@ var DIRECTION = 'next' /*********************************************************************/ var KEYBOARD_CONFIG = { + // info overlay... + // + // NOTE: this is here to prevent selecting images while trying to + // select info text... + '.overlay-info:hover': { + title: 'Info overlay', + doc: 'NOTE: when the cursor is over the info overlay one can use '+ + 'Ctrl-A and Ctrl-D for info text selection, without affecting '+ + 'image selection/marks.', + ignore: [ 'A' ], + A: { + // NOTE: this is here only for documentation... + ctrl: doc('Select all'), + }, + D: { + ctrl: doc('Clear selection', + function(){ + document.getSelection().empty() + return false + }) + } + }, + + // help mode... + // // NOTE: need to keep all info modes before the rest so as to give // their bindings priority... '.help-mode': { @@ -40,6 +65,7 @@ var KEYBOARD_CONFIG = { // single image mode only... + // '.single-image-mode': { title: 'Single image mode', @@ -59,6 +85,7 @@ var KEYBOARD_CONFIG = { // single image mode only... + // '.marked-only-view:not(.single-image-mode)': { title: 'Marked only view', @@ -72,6 +99,7 @@ var KEYBOARD_CONFIG = { // general setup... + // '.viewer:not(.overlay)': { title: 'Global', @@ -285,7 +313,7 @@ var KEYBOARD_CONFIG = { }, I: { // XXX group this with other info stuff into a single on/off toggle... - default: doc('Toggle image info visibility (on hover)', + default: doc('Toggle image info display', function(){ toggleImageInfo() }), // XXX STUB -- replace with a real info window... shift: doc('Show current image info', @@ -307,14 +335,30 @@ var KEYBOARD_CONFIG = { 'Position (global): '+ order +'/'+ DATA.order.length +'\n'+ '') }), + alt: doc('Toggle inline image info display', + function(){ + toggleInlineImageInfo() + }), + + // marking... ctrl: doc('Invert image marks', function(){ invertImageMarks() }), }, A: { - shift: doc('Toggle marks in current contagious block', - function(){ toggleImageMarkBlock() }), + // XXX does not yet work with DATA (???) + //shift: doc('Toggle marks in current contagious block', + // function(){ toggleImageMarkBlock() }), + ctrl: doc('Mark current ribbon', function(){ markAll('ribbon') }), + 'ctrl+shift': doc('Mark all images', + function(){ markAll('all') }), + }, + D: { + ctrl: doc('Unmark current ribbon', + function(){ removeImageMarks('ribbon') }), + 'ctrl+shift': doc('Unmark all images', + function(){ removeImageMarks('all') }), }, U: { ctrl: doc('Unmark current ribbon', diff --git a/ui/lib/keyboard.js b/ui/lib/keyboard.js index b54ba418..63530c22 100755 --- a/ui/lib/keyboard.js +++ b/ui/lib/keyboard.js @@ -62,6 +62,17 @@ var _SPECIAL_KEYS = { 188: ',', 190: '.', 191: '/', } +var _SHIFT_KEYS = { + '`': '~', '-': '_', '=':'+', + + 1: '!', 2: '@', 3: '#', 4: '$', 5: '%', + 6:'^', 7:'&', 8: '*', 9: '(', 0: ')', + + '[': '{', ']': '}i', '\\': '|', + ';': ':', '\'': '"', + ',': '<', '.': '>', '/': '?' +} + var _KEY_CODES = {} for(var k in _SPECIAL_KEYS){ _KEY_CODES[_SPECIAL_KEYS[k]] = k @@ -93,6 +104,7 @@ function toKeyCode(c){ // documentation wrapper... function doc(text, func){ + func = func == null ? function(){return true}: func func.doc = text return func } @@ -135,11 +147,12 @@ function doc(text, func){ * XXX BUG explicitly given modes do not yield results if the pattern * does not match... */ -function getKeyHandlers(key, modifiers, keybindings, modes){ +function getKeyHandlers(key, modifiers, keybindings, modes, shifted_keys){ var chr = null var did_handling = false modifiers = modifiers == null ? '' : modifiers modes = modes == null ? 'any' : modes + shifted_keys = shifted_keys == null ? _SHIFT_KEYS : shifted_keys if(typeof(key) == typeof(123)){ key = key @@ -149,6 +162,12 @@ function getKeyHandlers(key, modifiers, keybindings, modes){ key = toKeyCode(key) } + /* XXX this is not done yet... + if(shifted_keys != false && key in shifted_keys){ + key = shifted_keys[key] + } + */ + res = {} for(var mode in keybindings){ @@ -400,7 +419,8 @@ function makeKeyboardHandler(keybindings, unhandled){ * - list of key names. * */ -function buildKeybindingsHelp(keybindings){ +function buildKeybindingsHelp(keybindings, shifted_keys){ + shifted_keys = shifted_keys == null ? _SHIFT_KEYS : shifted_keys var res = {} var mode, title @@ -455,6 +475,13 @@ function buildKeybindingsHelp(keybindings){ var keys = [] section[doc] = keys } + + // translate shifted keys... + if(shifted_keys != false && mod == 'shift' && key in shifted_keys){ + mod = '' + key = shifted_keys[key] + } + keys.push((mod == '' || mod == 'default') ? key : (mod +'+'+ key)) } diff --git a/ui/modes.js b/ui/modes.js index 64e17c08..014aec23 100755 --- a/ui/modes.js +++ b/ui/modes.js @@ -69,6 +69,20 @@ var toggleTheme = createCSSClassToggler('.viewer', var toggleImageInfo = createCSSClassToggler('.viewer', '.image-info-visible') +var toggleInlineImageInfo = createCSSClassToggler('.viewer', + '.image-info-inline-visible', + function(action){ + if(action == 'on'){ + $(document) + .on('mouseover', inlineImageInfoHoverHandler) + } else { + $(document) + .off('mouseover', inlineImageInfoHoverHandler) + $('.inline-image-info').remove() + } + }) + + // NOTE: this confirmsto the css toggler protocol, but is not implemented // via createCSSClassToggler as we do not need to set any classes, // al least at this point...