NSArrayController select inserted indices after insertion not reflected in NSTableView - macos

I have two NSTableViews both bound to their own NSArrayControllers and the user can drag & drop items from table A to table B. Now I want the dropped items in table B to be selected after insertion (so that the table can scroll to them). But it's not working.
The table view selection indexes are bound to the array controller indexes in IB.
Select Inserted Objects is checked on the array controllers in IB.
If I check arrayControllerB.selectedObjects it lists only the first inserted object, even if more were inserted.
Selection is never reflected in the associated table view.
Now, I could try to temporarily store the newly inserted objects in an array and then select them in the arrayControllerB via setSelectedObjects(), like this ...
func insertIndicesToTargetAC(indexSet:NSIndexSet)
{
let a = indexSet.toArray();
var tmpObjects = [AnyObject]();
for i in a
{
let sourceItem = sourceAC.arrangedObjects.objectAtIndex(i);
let obj:NSManagedObject = createNewFromSourceItem(sourceItem);
targetAC.addObject(obj);
tmpObjects.append(obj);
}
sourceAC.removeObjectsAtArrangedObjectIndexes(indexSet);
targetAC.setSelectedObjects(tmpObjects);
println("\(targetAC.selectedObjects)");
}
}
Here, setSelectedObjects() does show me the correct amount of newly inserted objects in the target AC but it still doesn't reflect the selection in the associated table view. Can somebody inaugurate me into the holy sanctuary of trivial Cocoa operations that persistently refuse to work?

I figured it out. I shall not attempt to tableView.reloadData() after the insertion. That will erase the selection.

Related

Kendo treeview drag and drop always last position of parent

I am facing a problem in kendo treeview. I have a tree with 'folders'. I enabled the drag and drop functionality but whenever i drop a folder into another folder, it is positioned as the last item of that folder where it was dropped.
My datasource is of type kendo.data.HierarchicalDataSource and the incoming data is sorted by the 'caption'/text alphabetically. I want the dropped node to be sorted alfabetically as well, so in some way i want to trigger a resort call to the node where the other node was dropped.
How would i achieve this?
I already tried defining the sort parameter in the kendo.data.HierarchicalDataSource object but this doesn't change much.
Under the hood, a HierarchicalDataSource is a DataSource that has an array of children (items) that are also DataSources. Often, the items are just treated like a normal field, and operations on the main DataSource are not done recursively on the children.
Sorting is an example of this. As you see on https://docs.telerik.com/kendo-ui/controls/navigation/treeview/how-to/binding/sort-child-nodes, you need to sort the children recursively yourself:
function setSort(items){
for(var i=0; i < items.length; i++){
if(items[i].hasChildren){
items[i].children.sort({field: "FullName", dir: "desc"});
setSort(items[i].children.view());
}
}
}

How to apply a common dropdown value on multiple selected rows in a Webix datatable

I need a help on applying common drop down values to multiple selected rows in a Webix datatable. Let's say I want to select all the rows of my datatable (by Ctrl+click) for which the 'Name' column has value as 'Mark' and then want to apply a common color for them (for example : green) by clicking so that it gets applied on all the rows at one go.
The snippet is here : https://webix.com/snippet/1162ccd1
Any help on how can this be achieved would be appreciated.
Thanks in advance.
Further to this post, I am including a re-phrased and a half-way solution below for the experts to dispel any confusion about the requirement :
It does not necessarily have to be those rows which have 'Name' as 'Mark'. I put it that way to give an example. Basically, it could be any randomly selected row either consecutive or haphazard and selecting a common value for them from the drop down of the 'color' column (it could be any color in that drop down ) so that its value gets assigned to those cells of those selected rows. Note, selecting a color should not change the color of the row, so there is no css effect I want here.
I have so far written the code like below, which is able to fetch the selected rows.
rows = $$("mytable").getSelectedId(true);
for (i in rows) {
id = rows[i];
item = $$("mytable").getItem(id);
/* below effect has to happen from the drop down of data table gui */
item.id2 = "green"; //for example, it could be any value
}
Can anybody please help me in:
i) how can I apply the value selected from the drop down of data table to all the selected rows ?
ii) Secondly, how can I trigger this code ( through which event ?) once they are selected in the data table and the value is chosen from the drop down ?
Thanks.
You can do something like this by adding the onBeforeFilter event, and track only color filter ("id2" in your snippet):
onBeforeFilter: function(id, value, config) {
if (id === "id2") {
const selected = this.getSelectedId(true);
const color = this.getFilter("id2").value;
for (let row in selected) {
const item = this.getItem(selected[row]);
item["id2"] = color;
this.updateItem(item.id, item);
}
}
}
Sample is here https://webix.com/snippet/538e1ca0
But I think this is not the best way.
You can also create a context menu that is displayed by right-clicking on the table and making a choice of values in it.

JavaFX sorted tableview how to re-sort to default sort order on redisplay

I have a JavaFX sorted tableview - defined as follows in the overridden initialize method:
// Bind the FX tableview to the data - allowing sorting
final SortedList<LoadedWotsitModel> sortedList =
new SortedList<LoadedWotsitModel>(configurationDataManager.getLoadedWotsit());
tableLoadedWotsit.setItems(sortedList);
sortedList.comparatorProperty().bind(tableLoadedWotsit.comparatorProperty());
// Sort by time
colTime.setSortType(TableColumn.SortType.ASCENDING);
tableLoadedWotsit.getSortOrder().add(colTime);
This works fine, other columns in the table are also sortable by the user.
When the dialog is closed and re-opened, it is sorted in the order the user last chose. I want to be able to make it sort by my default colTime column every time the dialog is displayed (not the previously chosen user sort order).
What event can I hook into to re-sort the table when it is displayed?
My solution was to use a menu controller and invoke the dialog in code thus:
myDialogController.sortByTimeAscending();
myDialog.show();
Allowing me to call a method in the controller directly that performs the sorting.
/**
* Sort by time (ascending).
*/
public void sortByTimeAscending()
{
colTime.setSortType(TableColumn.SortType.ASCENDING);
tableLoadedWotsit.getSortOrder().clear();
tableLoadedWotsit.getSortOrder().add(colTime);
}

jqGrid: update a row AND have its formatters updated as well

Ideally I would call setRowData and have my cell formatters and rowattr function re-run, but cell formatters and rowattr functions don't re-run when calling setRowData (I don't know why but that's another question maybe), so setRowData isn't really helpful for me.
It seems the next easiest thing to do would be to remove a row and re-add a new one at the same position with the same model. To do that I need to get the rowid of the row above the selected row so that I can call addRowData and specify the ID of the row above in srcrowid and use 'after' for the position. This is what I'm thinking:
$.jgrid.extend({
updateRow: function(rowid, model){
// get index from id
var index = this.jqGrid('getInd', rowid);
// note: my first row's index is 1 (is that normal?)
if ( index == 1 ){
position = 'first';
srcrowid = 'n/a';
}
else{
position = 'after';
srcrowid = _____ how to get rowid of row above selected row _____???
}
// delete row
this.jqGrid('delRowData', rowid);
// insert at index
this.jqGrid('addRowData', rowid, model.attributes, position, srcrowid);
}
});
How can I get the rowid of the row above the selected row? (Is there an easier way? Is this a bad strategy?)
Note: I'm using backbone.js collections and models
I find the best way to change the row is to use setRowData instead of usage delRowData and addRowData. If you know rowid then you can use $("#" + rowid); (or if rowis have special characters like :, . an so on then $("#" + $.jgrid.jqID(rowid));) to get the <tr> element. Then you can use jQuery.addClass, jQuery.css, jQuery.attr to change the attributes of the row.
It's important to understand that jqGrid uses the same methods internally it it's required to modify element of the grid. The main goal of rowattr is another one. During filling of the grid data there are many scenarios. One can create DOM elements for <td> and <td> and insert there in the grid. The main problem is performance in case of working with DOM. It's much slowly as building of strings. Moreover DOM is even much more slowly if the elements are exist on the the HTML page (in opposite to disconnected elements). If one modify one element only from the grid having 500 rows then the position of elements of all other rows need be recalculated.
Because of the problem jqGrid construct the whole body of the grid first in string format and then assign all <tr> and <td> elements using one set of innerHTML. It improves dramatically the performance of filling of the grid. See the answer for additional information. The formatters and callbacks cellattr and rowattr are introduced to allow to customize cell and row attributes during building of grid body in string format. It gives you customization possibilities without reducing of performance.
On the other side if you need to modify the row which are attached already on HTML page then you will have no advantage with working with strings instead of DOM. Because of that I recommend you just use jQuery.addClass, jQuery.css, jQuery.attr directly. If you need to change multiple classes, assign multiple css rules or multiple attributes then you should use one call of above functions. You can use object form of jQuery.css, jQuery.attr for it.
The updateRow extension below works, BUT I ended up not using it. Not so much for the reasons Oleg talked about (which I assume are valid and something you should definitely consider), but because I had a filter provider that was too difficult to sync with (e.g. after soft deleting a row, I need to now determine if new deleted status agrees with current filter...and that's a pretty simple example). So I just defer to the data the filter provider gives me and repopulate the grid each time, which I'm not fond of, but I don't see other easy options.
As far as the extension below goes, here are pros/cons as I see them:
Pros:
easy to use
You just rely on the rowattr functions and cell formatters you already defined. You don't have to write those twice.
Cons:
possibly performance--see Oleg's answer
please add any you see
Unknowns:
performance? I have no idea how much worse it performs. It would be interesting to do benchmarks with different browsers. I saw no problems, but I only have maybe 20 rows. Let's say we're working with 500 rows and adding/removing classes and calling .css() took 30 ms but using the extension took 300 ms on a 'typical' machine/browser. That would be 10 times slower, but for me it would probably be worth it because I don't have to write something twice.
here's the extension:
$.jgrid.extend({
updateRow: function(rowid, data){
// get index from id
var index = this.jqGrid('getInd', rowid);
// note: my first row's index is 1 (is that normal?)
if ( index == 1 ){
position = 'first';
}
else{
position = 'after';
srcrowid = $(this).find('tr#' + rowid).prev()[0].id;
}
// delete row
this.jqGrid('delRowData', rowid);
// insert at index
this.jqGrid('addRowData', rowid, data, position, srcrowid);
}
});

NSTextField in NSTableCellView - end editing on loss of focus

I have a view with a view-based NSTableView (which itself has a cell view with a single text field) and some buttons and textfields outside the tableview. One of the buttons adds an object into the datasource for the tableview, and after inserting the row into the tableview, immediately makes it editable.
If the user enters the text and pressed the return key, I receive the - (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor delegate method fine, and I can run my validation and save the value. But the delegate doesn't get called if the user selects any of the other buttons or textfields outside the tableview.
What's the best way to detect this loss-of-focus on the textfield inside the NSTableCellView, so I can run some of my validation code on the tableview entry?
If I understand you correctly you want a control:textShouldEndEditing: notification to fire in the following situation:
You add a new object to the array controller.
The row in the table representing the object is automatically selected.
YOU programmatically select the text field in the relevant row for editing.
The user immediately (i.e. without making any edits in the text field) gives focus to a control elsewhere in the UI
One approach I've used in the past to get this working is to make an insignificant programmatic change to the field editor associated with the text field, just before the text field becomes available to the user for editing. The snippet below shows how to do this - this is step 2/step 3 in the above scenario:
func tableViewSelectionDidChange(notification: NSNotification) {
if justAddedToArrayController == true {
// This change of selection is occurring because the user has added a new
// object to the array controller, and it has been automatically selected
// in the table view. Now need to give focus to the text field in the
// newly selected row...
// Access the cell
var cell = tableView.viewAtColumn(0,
row: arrayController.selectionIndex,
makeIfNecessary: true) as NSTableCellView
// Make the text field associated with the cell the first responder (i.e.
// give it focus)
window.makeFirstResponder(cell.textField!)
// Access, then 'nudge' the field editor - make it think it's already
// been edited so that it'll fire 'should' messages even if the user
// doesn't add anything to the text field
var fe = tableView.window?.fieldEditor(true, forObject: cell.textField!)
fe!.insertText(cell.textField!.stringValue)
}
}

Resources