diff --git a/phpgwapi/js/egw_action/egw_action.js b/phpgwapi/js/egw_action/egw_action.js index e5aed6f0cb..ef46e83bf2 100644 --- a/phpgwapi/js/egw_action/egw_action.js +++ b/phpgwapi/js/egw_action/egw_action.js @@ -1027,7 +1027,8 @@ egwActionObject.prototype.getIndex = function() */ egwActionObject.prototype.getFocusedObject = function() { - var cr = this.getContainerRoot(); + /*var cr = this.getContainerRoot();*/ + var cr = this.getRootObject(); return cr ? cr.focusedChild : null; } @@ -1113,6 +1114,115 @@ egwActionObject.prototype._ifaceCallback = function(_newState, _changedBit, _shi return _newState; } +/** + * Handler for key presses + */ +egwActionObject.prototype.handleKeyPress = function(_keyCode, _shift, _ctrl, _alt) { + switch (_keyCode) { + case EGW_KEY_ARROW_UP: + case EGW_KEY_ARROW_DOWN: + case EGW_KEY_PAGE_UP: + case EGW_KEY_PAGE_DOWN: + var intval = + (_keyCode == EGW_KEY_ARROW_UP || _keyCode == EGW_KEY_ARROW_DOWN) ? + 1 : 10; + + if (this.children.length > 0) + { + // Get the focused object + var focused = this.getFocusedObject(); + + // Determine the object which should get selected + var selObj = null; + if (!focused) + { + selObj = this.children[0]; + } + else + { + selObj = (_keyCode == EGW_KEY_ARROW_UP || _keyCode == EGW_KEY_PAGE_UP) ? + focused.getPrevious(intval) : focused.getNext(intval); + } + + if (selObj != null) + { + if (!_shift) + { + this.setAllSelected(false); + } + else + { + var objs = focused.traversePath(selObj); + for (var i = 0; i < objs.length; i++) + { + objs[i].setSelected(true); + } + } + + selObj.setSelected(true); + selObj.setFocused(true); + + // Tell the aoi of the object to make it visible + selObj.makeVisible(); + } + + return true; + } + + break; + + // Handle CTRL-A to select all elements in the current container + case EGW_KEY_A: + if (_ctrl) + { + this.setAllSelected(true); + return true; + } + + break; + } + + return false; +} + +egwActionObject.prototype.getPrevious = function(_intval) +{ + if (this.parent != null) + { + var idx = this.parent.children.indexOf(this); + if (idx > 0) + { + idx = Math.max(0, idx - _intval); + return this.parent.children[idx]; + } + else + { + // TODO: Implement traversal of trees + } + } + + return this; +} + +egwActionObject.prototype.getNext = function(_intval) +{ + if (this.parent != null) + { + var idx = this.parent.children.indexOf(this); + if (idx < this.parent.children.length - 1) + { + idx = Math.min(this.parent.children.length - 1, idx + _intval); + return this.parent.children[idx]; + } + else + { + // TODO: Implement traversal of trees + } + } + + return this; +} + /** * Returns whether the object is currently selected. */ @@ -1175,6 +1285,11 @@ egwActionObject.prototype.setFocused = function(_focused) this.parent.updateFocusedChild(this, _focused); } } + + if (this.focusedChild != null && _focused == false) + { + this.focusedChild.setFocused(false); + } } /** @@ -1289,7 +1404,7 @@ egwActionObject.prototype.updateFocusedChild = function(_child, _focused) } } - if (this.parent && !egwBitIsSet(this.flags, EGW_AO_FLAG_IS_CONTAINER)) + if (this.parent /*&& !egwBitIsSet(this.flags, EGW_AO_FLAG_IS_CONTAINER)*/) { this.parent.updateFocusedChild(_child, _focused); } @@ -1411,6 +1526,15 @@ egwActionObject.prototype.triggerCallback = function() return true; } +/** + * Calls the corresponding function of the AOI which tries to make the object + * visible. + */ +egwActionObject.prototype.makeVisible = function() +{ + this.iface.makeVisible(); +} + var EGW_AO_EXEC_SELECTED = 0; var EGW_AO_EXEC_THIS = 1; @@ -1660,6 +1784,8 @@ function egwActionObjectInterface() // or EGW_AI_DRAG_OUT this.doTriggerEvent = function(_event, _data) {return false;} + this.doMakeVisible = function() {}; + this._state = EGW_AO_STATE_NORMAL || EGW_AO_STATE_VISIBLE; this.stateChangeCallback = null; @@ -1778,8 +1904,13 @@ egwActionObjectInterface.prototype.triggerEvent = function(_event, _data) return this.doTriggerEvent(_event, _data); } - - +/** + * Scrolls the element into a visble area if it is currently hidden + */ +egwActionObjectInterface.prototype.makeVisible = function() +{ + return this.doMakeVisible(); +} /** -- egwActionObjectDummyInterface Class -- **/ diff --git a/phpgwapi/js/egw_action/egw_grid_data.js b/phpgwapi/js/egw_action/egw_grid_data.js index 1f39d402d8..f7ac5c9ced 100644 --- a/phpgwapi/js/egw_action/egw_grid_data.js +++ b/phpgwapi/js/egw_action/egw_grid_data.js @@ -445,8 +445,10 @@ egwGridDataElement.prototype.insertElement = function(_index, _id) null); element.index = _index; - // Create the action object - var object = this.actionObject.insertObject(_index, _id, null, 0); + // Create the action object with a temporary AOI which can be used to make + // the object visible + var object = this.actionObject.insertObject(_index, _id, + new egwGridTmpAOI(this.getRootElement().gridObject, _index), 0); object.data = element; // Link the two together diff --git a/phpgwapi/js/egw_action/egw_grid_view.js b/phpgwapi/js/egw_action/egw_grid_view.js index e0ec3dc9c1..ae9639f521 100644 --- a/phpgwapi/js/egw_action/egw_grid_view.js +++ b/phpgwapi/js/egw_action/egw_grid_view.js @@ -127,6 +127,8 @@ function egwGridViewOuter(_parentNode, _dataRoot, _selectColsCallback, _toggleAl // Insert the base grid container into the DOM-Tree this.grid = new egwGridViewGrid(null, null, true, this); // (No parent grid, no height change callback, scrollable) this.grid.insertIntoDOM(this.outer_tr, []); + + this.dataRoot.gridObject = this.grid; } /** @@ -1435,6 +1437,7 @@ function egwGridViewRow_aoiSetup() this.aoi.row = this; this.aoi.doSetState = egwGridViewRow_aoiSetState; this.aoi.doTriggerEvent = egwGridViewRow_aoiTriggerEvent; + this.aoi.doMakeVisible = egwGridViewRow_aoiMakeVisible; this.aoi.getDOMNode = egwGridViewRow_aoiGetDOMNode; } @@ -1473,6 +1476,11 @@ function egwGridViewRow_aoiTriggerEvent(_event, _data) } } +function egwGridViewRow_aoiMakeVisible() +{ + egwGridView_scrollToArea(this.row.grid.scrollarea, this.row.getArea()); +} + /** * Returns the actionObjectInterface object of this grid item. */ @@ -2041,4 +2049,54 @@ function egwGridViewFullRow_doSetViewArea() // } +/** + * Temporary AOI which has to be assigned to invisible grid objects in order + * to give them the possiblity to make them visible when using e.g. keyboard navigation + */ +function egwGridTmpAOI(_grid, _index) +{ + var aoi = new egwActionObjectDummyInterface(); + + // Assign the make visible function + aoi.grid = _grid; + aoi.index = _index; + aoi.doMakeVisible = egwGridTmpAOI_makeVisible; + + return aoi; +} + +function egwGridTmpAOI_makeVisible() +{ + // Assume an area for the element (this code is not optimal, but it should + // work in most cases - problem is that the elements in the grid may have equal + // sizes and the grid is scrolled to some area where the element is not) + // TODO: Support for trees + var avgHeight = this.grid.getOuter().avgRowHeight; + var area = egwArea(this.index * avgHeight, avgHeight); + + egwGridView_scrollToArea(this.grid.scrollarea, area); +} + +function egwGridView_scrollToArea(_scrollarea, _visarea) +{ + // Get the current view area + var va = egwArea(_scrollarea.scrollTop(), _scrollarea.height()); + + // Calculate the assumed position of this element + var pos = _visarea; + + // Check whether it is currently (completely) visible, if not scroll the + // scroll area to that position + if (!(pos.top >= va.top && pos.bottom <= va.bottom)) + { + if (pos.top < va.top) + { + _scrollarea.scrollTop(pos.top); + } + else + { + _scrollarea.scrollTop(va.top + pos.bottom - va.bottom); + } + } +}