Skip to content

Instantly share code, notes, and snippets.

@romualdrichard
Last active February 5, 2026 13:34
Show Gist options
  • Select an option

  • Save romualdrichard/021185373284b30c26a0656932b86ff0 to your computer and use it in GitHub Desktop.

Select an option

Save romualdrichard/021185373284b30c26a0656932b86ff0 to your computer and use it in GitHub Desktop.
#jarchi Create View from Properties
/*
* jArchi Script – Filtered Views Manager
* ─────────────────────────────────────────
* Tab 1 – "Filter Views" : create or update a single view by property filters
* Tab 2 – "Manage Views" : scan model, list every view that carries saved
* criteria, and batch-update them. Each row has its
* own "Synchronize" checkbox (default ON).
*
* Filter logic
* same property, several values → OR
* different properties → AND
* exclusion filters → NOT (any match → reject)
*
* Criteria are stored in view.documentation inside
* <!-- FILTER_CRITERIA_START … FILTER_CRITERIA_END -->
*/
console.clear();
console.show();
// ─── Java / SWT imports ──────────────────────────────────────────────────────
var SWT = Java.type("org.eclipse.swt.SWT");
var GridLayout = Java.type("org.eclipse.swt.layout.GridLayout");
var GridData = Java.type("org.eclipse.swt.layout.GridData");
var Shell = Java.type("org.eclipse.swt.widgets.Shell");
var Display = Java.type("org.eclipse.swt.widgets.Display");
var Label = Java.type("org.eclipse.swt.widgets.Label");
var ListWidget = Java.type("org.eclipse.swt.widgets.List");
var Button = Java.type("org.eclipse.swt.widgets.Button");
var MessageBox = Java.type("org.eclipse.swt.widgets.MessageBox");
var Composite = Java.type("org.eclipse.swt.widgets.Composite");
var Group = Java.type("org.eclipse.swt.widgets.Group");
var Combo = Java.type("org.eclipse.swt.widgets.Combo");
var TabFolder = Java.type("org.eclipse.swt.widgets.TabFolder");
var TabItem = Java.type("org.eclipse.swt.widgets.TabItem");
var SelectionAdapter = Java.type("org.eclipse.swt.events.SelectionAdapter");
// ─── persistence markers ─────────────────────────────────────────────────────
var CRITERIA_START = "<!-- FILTER_CRITERIA_START";
var CRITERIA_END = "FILTER_CRITERIA_END -->";
// ═══════════════════════════════════════════════════════════════════════════════
// SHARED UTILITIES
// ═══════════════════════════════════════════════════════════════════════════════
function saveCriteriaToView(view, inc, exc, targetType) {
var block = CRITERIA_START + "\n" +
JSON.stringify({ targetType: targetType, inclusion: inc, exclusion: exc, timestamp: new Date().toISOString() }) +
"\n" + CRITERIA_END;
var doc = view.documentation || "";
var s = doc.indexOf(CRITERIA_START), e = doc.indexOf(CRITERIA_END);
if (s !== -1 && e !== -1) doc = doc.substring(0, s) + doc.substring(e + CRITERIA_END.length);
view.documentation = doc.trim() + "\n\n" + block;
}
function loadCriteriaFromView(view) {
var doc = view.documentation || "";
var s = doc.indexOf(CRITERIA_START), e = doc.indexOf(CRITERIA_END);
if (s === -1 || e === -1) return null;
try { return JSON.parse(doc.substring(s + CRITERIA_START.length, e).trim()); }
catch (err) { console.log("Parse error in '" + view.name + "': " + err); return null; }
}
function organizeFiltersByProperty(filters) {
var map = {};
for (var i = 0; i < filters.length; i++) {
var f = filters[i];
if (!map[f.propName]) map[f.propName] = [];
map[f.propName].push(f.propValue);
}
return map;
}
function getAllPropertiesWithValues(targetType) {
var propsMap = {};
$(targetType).each(function(item) {
var names = $(item).prop();
names.forEach(function(n) {
var v = $(item).prop(n) || "(empty)";
if (!propsMap[n]) propsMap[n] = {};
propsMap[n][v] = true;
});
});
return propsMap;
}
function getAllViews() {
var views = [];
$("view").each(function(v) { views.push(v); });
return views;
}
function toJavaStringArray(arr) {
var ja = java.lang.reflect.Array.newInstance(java.lang.String, arr.length);
for (var i = 0; i < arr.length; i++) ja[i] = arr[i];
return ja;
}
// ─── matching ────────────────────────────────────────────────────────────────
function matchesFilters(concept, inc, exc) {
var incMap = organizeFiltersByProperty(inc);
var excMap = organizeFiltersByProperty(exc);
for (var p in incMap) {
var val = $(concept).prop(p) || "(empty)";
var ok = false;
for (var i = 0; i < incMap[p].length; i++) { if (val === incMap[p][i]) { ok = true; break; } }
if (!ok) return false;
}
for (var p in excMap) {
var val = $(concept).prop(p) || "(empty)";
for (var i = 0; i < excMap[p].length; i++) { if (val === excMap[p][i]) return false; }
}
return true;
}
function findElements(inc, exc) {
var r = [];
$("element").each(function(el) { if (matchesFilters(el, inc, exc)) r.push(el); });
return r;
}
function findRelationships(inc, exc) {
var r = [];
$("relationship").each(function(rel) { if (matchesFilters(rel, inc, exc)) r.push(rel); });
return r;
}
// ─── view engine ─────────────────────────────────────────────────────────────
function applyToView(existingView, elements, relationships, inc, exc, targetType, synchronize) {
var view, isNew = (existingView === null);
if (isNew) {
var incMap = organizeFiltersByProperty(inc), excMap = organizeFiltersByProperty(exc), parts = [];
for (var p in incMap) parts.push(incMap[p].length === 1 ? p + "=" + incMap[p][0] : p + " IN (" + incMap[p].join(", ") + ")");
for (var p in excMap) parts.push(excMap[p].length === 1 ? "NOT " + p + "=" + excMap[p][0] : "NOT " + p + " IN (" + excMap[p].join(", ") + ")");
var prefix = targetType === "element" ? "Elements" : targetType === "relationship" ? "Relationships" : "Mixed";
var viewName = "Filtered " + prefix + " - " + parts.join(" AND ");
if (viewName.length > 150) viewName = viewName.substring(0, 147) + "...";
view = model.createArchimateView(viewName);
} else {
view = existingView;
}
saveCriteriaToView(view, inc, exc, targetType);
// snapshot current view contents
var existElIds = {}, existRelIds = {}, viewObjs = {}, existElArr = [], existRelArr = [];
$(view).find("element").each(function(vo) {
var c = vo.concept;
if (c) { existElIds[c.id] = true; viewObjs[c.id] = vo; existElArr.push({concept:c, viewObj:vo}); }
});
$(view).find("relationship").each(function(vo) {
var c = vo.concept;
if (c) { existRelIds[c.id] = true; existRelArr.push({concept:c, viewObj:vo}); }
});
// ── synchronize: remove non-matching ──
var removedEl = 0, removedRel = 0;
if (synchronize && !isNew) {
// relationships first (avoid dangling)
for (var i = 0; i < existRelArr.length; i++) {
var it = existRelArr[i];
if (!matchesFilters(it.concept, inc, exc)) {
try { it.viewObj.delete(); delete existRelIds[it.concept.id]; removedRel++; }
catch (err) { console.log("Cannot remove rel: " + err); }
}
}
for (var i = 0; i < existElArr.length; i++) {
var it = existElArr[i];
if (!matchesFilters(it.concept, inc, exc)) {
try { it.viewObj.delete(); delete existElIds[it.concept.id]; delete viewObjs[it.concept.id]; removedEl++; }
catch (err) { console.log("Cannot remove el: " + err); }
}
}
}
// collect all elements needed (including rel endpoints)
var elMap = {};
for (var i = 0; i < elements.length; i++) elMap[elements[i].id] = elements[i];
for (var i = 0; i < relationships.length; i++) {
var r = relationships[i];
if (r.source) elMap[r.source.id] = r.source;
if (r.target) elMap[r.target.id] = r.target;
}
// position new elements below existing ones
var x = 50, y = 50, maxPerRow = 5, count = 0;
if (Object.keys(viewObjs).length > 0) {
var maxY = 0;
for (var id in viewObjs) { var b = viewObjs[id].bounds; if (b && (b.y + b.height) > maxY) maxY = b.y + b.height; }
y = maxY + 100;
x = 50; // start fresh row
count = 0; // reset counter
}
var addedEl = 0;
for (var id in elMap) {
if (existElIds[id]) continue;
viewObjs[id] = view.add(elMap[id], x, y, 120, 55);
addedEl++; count++;
if (count % maxPerRow === 0) { x = 50; y += 100; } else { x += 150; }
}
var addedRel = 0;
for (var i = 0; i < relationships.length; i++) {
var r = relationships[i];
if (existRelIds[r.id]) continue;
if (r.source && r.target && viewObjs[r.source.id] && viewObjs[r.target.id]) {
try { view.add(r, viewObjs[r.source.id], viewObjs[r.target.id]); addedRel++; }
catch (err) { console.log("Cannot add rel: " + err); }
}
}
console.log("'" + view.name + "' → added " + (addedEl+addedRel) + ", removed " + (removedEl+removedRel));
view.openInUI();
return { added: addedEl + addedRel, removed: removedEl + removedRel };
}
// ─── criteria display ────────────────────────────────────────────────────────
function formatCriteria(criteria) {
var parts = [], tLabel = criteria.targetType === "element" ? "Elements" : criteria.targetType === "relationship" ? "Relationships" : "Both";
var incMap = organizeFiltersByProperty(criteria.inclusion || []);
var excMap = organizeFiltersByProperty(criteria.exclusion || []);
for (var p in incMap) parts.push(incMap[p].length === 1 ? p + "=" + incMap[p][0] : p + " IN (" + incMap[p].join(", ") + ")");
for (var p in excMap) parts.push(excMap[p].length === 1 ? "NOT " + p + "=" + excMap[p][0] : "NOT " + p + " IN (" + excMap[p].join(", ") + ")");
var txt = tLabel + " | " + (parts.length > 0 ? parts.join(" AND ") : "(no filters)");
return txt.length > 150 ? txt.substring(0, 147) + "..." : txt;
}
// ═══════════════════════════════════════════════════════════════════════════════
// TAB 1 – FILTER VIEWS (create / update a single view)
// ═══════════════════════════════════════════════════════════════════════════════
function buildTab1(tabFolder) {
var tab1 = new TabItem(tabFolder, SWT.NONE);
tab1.setText("Filter Views");
var page1 = new Composite(tabFolder, SWT.NONE);
tab1.setControl(page1);
var pl = new GridLayout(1, false); pl.marginWidth = 8; pl.marginHeight = 8;
page1.setLayout(pl);
// ── Target Type ──
var typeGroup = new Group(page1, SWT.NONE);
typeGroup.setText("Target Type");
typeGroup.setLayout(new GridLayout(3, true));
typeGroup.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
var radioElements = new Button(typeGroup, SWT.RADIO); radioElements.setText("Elements"); radioElements.setSelection(true);
var radioRelationships = new Button(typeGroup, SWT.RADIO); radioRelationships.setText("Relationships");
var radioBoth = new Button(typeGroup, SWT.RADIO); radioBoth.setText("Both");
// ── View Selection ──
var viewGroup = new Group(page1, SWT.NONE);
viewGroup.setText("View Selection");
viewGroup.setLayout(new GridLayout(2, false));
viewGroup.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
var radioCreateNew = new Button(viewGroup, SWT.RADIO); radioCreateNew.setText("Create New View"); radioCreateNew.setSelection(true);
radioCreateNew.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
var radioUpdate = new Button(viewGroup, SWT.RADIO); radioUpdate.setText("Update Existing View:");
radioUpdate.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
new Label(viewGroup, SWT.NONE).setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
var comboViews = new Combo(viewGroup, SWT.READ_ONLY | SWT.BORDER);
comboViews.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
comboViews.setEnabled(false);
new Label(viewGroup, SWT.NONE).setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
var checkSync = new Button(viewGroup, SWT.CHECK);
checkSync.setText("Synchronize (remove items that no longer match the filters)");
checkSync.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
checkSync.setEnabled(false);
checkSync.setSelection(true);
// populate combo
var allViews = getAllViews();
var viewNames = [];
for (var i = 0; i < allViews.length; i++) viewNames.push(allViews[i].name);
viewNames.sort();
comboViews.setItems(toJavaStringArray(viewNames));
// ── mutable state ──
var currentTargetType = "element";
var propsMap = getAllPropertiesWithValues("element");
var inclusionFilters = [];
var exclusionFilters = [];
// ── filter panels (inclusion | exclusion) ──
var mainComp = new Composite(page1, SWT.NONE);
mainComp.setLayout(new GridLayout(2, true));
mainComp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
function buildFilterPanel(parent, title) {
var grp = new Group(parent, SWT.NONE);
grp.setText(title);
grp.setLayout(new GridLayout(3, false));
grp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
new Label(grp, SWT.NONE).setText("Properties:");
new Label(grp, SWT.NONE).setText("Values (Ctrl+click = OR):");
new Label(grp, SWT.NONE).setText("Active:");
var listProps = new ListWidget(grp, SWT.BORDER | SWT.SINGLE | SWT.V_SCROLL);
var gd1 = new GridData(SWT.FILL, SWT.FILL, false, true); gd1.widthHint = 140; listProps.setLayoutData(gd1);
var listValues = new ListWidget(grp, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL);
listValues.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
listValues.setEnabled(false);
var listActive = new ListWidget(grp, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL);
var gd2 = new GridData(SWT.FILL, SWT.FILL, false, true); gd2.widthHint = 150; listActive.setLayoutData(gd2);
new Label(grp, SWT.NONE).setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
var btnAdd = new Button(grp, SWT.PUSH); btnAdd.setText("Add →");
btnAdd.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); btnAdd.setEnabled(false);
var btnRemove = new Button(grp, SWT.PUSH); btnRemove.setText("← Remove");
btnRemove.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); btnRemove.setEnabled(false);
return { listProps: listProps, listValues: listValues, listActive: listActive, btnAdd: btnAdd, btnRemove: btnRemove };
}
var incPanel = buildFilterPanel(mainComp, "Inclusion Filters (INCLUDE)");
var excPanel = buildFilterPanel(mainComp, "Exclusion Filters (EXCLUDE)");
// ── Execute ──
var btnRow = new Composite(page1, SWT.NONE);
btnRow.setLayout(new GridLayout(1, false));
btnRow.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, false));
var btnExecute = new Button(btnRow, SWT.PUSH); btnExecute.setText("Execute");
var gdEx = new GridData(SWT.FILL, SWT.CENTER, false, false); gdEx.widthHint = 120; btnExecute.setLayoutData(gdEx);
// ═══ wiring ══════════════════════════════════════════════════════════════
function reloadProps() {
var names = Object.keys(propsMap).sort(), ja = toJavaStringArray(names);
incPanel.listProps.setItems(ja); incPanel.listValues.removeAll(); incPanel.listValues.setEnabled(false);
excPanel.listProps.setItems(ja); excPanel.listValues.removeAll(); excPanel.listValues.setEnabled(false);
}
function refreshPropsMap() {
propsMap = getAllPropertiesWithValues(currentTargetType === "both" ? "element" : currentTargetType);
reloadProps();
}
reloadProps();
function wirePanel(panel, filtersArr, prefix) {
panel.listProps.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
var idx = panel.listProps.getSelectionIndex();
if (idx < 0) return;
var pName = panel.listProps.getItem(idx);
if (propsMap[pName]) {
panel.listValues.removeAll();
panel.listValues.setItems(toJavaStringArray(Object.keys(propsMap[pName]).sort()));
panel.listValues.setEnabled(true);
panel.btnAdd.setEnabled(false);
}
}
});
panel.listValues.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) { panel.btnAdd.setEnabled(panel.listValues.getSelectionCount() > 0); }
});
panel.listActive.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) { panel.btnRemove.setEnabled(panel.listActive.getSelectionCount() > 0); }
});
function refreshActive() {
var items = [];
for (var i = 0; i < filtersArr.length; i++)
items.push((prefix ? prefix + " " : "") + filtersArr[i].propName + " = " + filtersArr[i].propValue);
panel.listActive.setItems(toJavaStringArray(items));
panel.btnRemove.setEnabled(false);
}
panel.btnAdd.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
var pIdx = panel.listProps.getSelectionIndex();
if (pIdx < 0) return;
var pName = panel.listProps.getItem(pIdx);
var indices = panel.listValues.getSelectionIndices();
for (var i = 0; i < indices.length; i++)
filtersArr.push({ propName: pName, propValue: panel.listValues.getItem(indices[i]) });
refreshActive();
panel.listValues.deselectAll();
panel.btnAdd.setEnabled(false);
}
});
panel.btnRemove.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
var indices = panel.listActive.getSelectionIndices();
for (var i = indices.length - 1; i >= 0; i--) filtersArr.splice(indices[i], 1);
refreshActive();
}
});
panel.refreshActive = refreshActive;
}
wirePanel(incPanel, inclusionFilters, "");
wirePanel(excPanel, exclusionFilters, "NOT");
// target-type radios
radioElements.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) { if (radioElements.getSelection()) { currentTargetType = "element"; refreshPropsMap(); } }
});
radioRelationships.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) { if (radioRelationships.getSelection()) { currentTargetType = "relationship"; refreshPropsMap(); } }
});
radioBoth.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) { if (radioBoth.getSelection()) { currentTargetType = "both"; refreshPropsMap(); } }
});
// view-mode radios
radioCreateNew.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) { comboViews.setEnabled(false); checkSync.setEnabled(false); }
});
radioUpdate.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) { comboViews.setEnabled(true); checkSync.setEnabled(true); }
});
// combo → load saved criteria
comboViews.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
var idx = comboViews.getSelectionIndex();
if (idx < 0) return;
var selName = comboViews.getItem(idx);
for (var i = 0; i < allViews.length; i++) {
if (allViews[i].name !== selName) continue;
var saved = loadCriteriaFromView(allViews[i]);
if (!saved) break;
if (saved.targetType === "element") { radioElements.setSelection(true); radioRelationships.setSelection(false); radioBoth.setSelection(false); currentTargetType = "element"; }
else if (saved.targetType === "relationship") { radioElements.setSelection(false); radioRelationships.setSelection(true); radioBoth.setSelection(false); currentTargetType = "relationship"; }
else { radioElements.setSelection(false); radioRelationships.setSelection(false); radioBoth.setSelection(true); currentTargetType = "both"; }
refreshPropsMap();
inclusionFilters.length = 0; exclusionFilters.length = 0;
if (saved.inclusion) for (var j = 0; j < saved.inclusion.length; j++) inclusionFilters.push(saved.inclusion[j]);
if (saved.exclusion) for (var j = 0; j < saved.exclusion.length; j++) exclusionFilters.push(saved.exclusion[j]);
incPanel.refreshActive();
excPanel.refreshActive();
var mb = new MessageBox(page1.getShell(), SWT.ICON_INFORMATION | SWT.OK);
mb.setText("Criteria Loaded");
mb.setMessage("Inclusion: " + inclusionFilters.length + " Exclusion: " + exclusionFilters.length);
mb.open();
break;
}
}
});
// Execute
btnExecute.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
if (inclusionFilters.length === 0 && exclusionFilters.length === 0) {
var mb = new MessageBox(page1.getShell(), SWT.ICON_WARNING | SWT.OK);
mb.setText("Empty Filters"); mb.setMessage("Add at least one inclusion or exclusion filter."); mb.open();
return;
}
var existingView = null, synchronize = false;
if (radioUpdate.getSelection()) {
synchronize = checkSync.getSelection();
var idx = comboViews.getSelectionIndex();
if (idx < 0) {
var mb = new MessageBox(page1.getShell(), SWT.ICON_WARNING | SWT.OK);
mb.setText("No View"); mb.setMessage("Select a view to update."); mb.open();
return;
}
var selName = comboViews.getItem(idx);
for (var i = 0; i < allViews.length; i++) { if (allViews[i].name === selName) { existingView = allViews[i]; break; } }
}
var elements = [], relationships = [];
if (currentTargetType === "element" || currentTargetType === "both") elements = findElements(inclusionFilters, exclusionFilters);
if (currentTargetType === "relationship" || currentTargetType === "both") relationships = findRelationships(inclusionFilters, exclusionFilters);
if (elements.length === 0 && relationships.length === 0) {
var mb = new MessageBox(page1.getShell(), SWT.ICON_WARNING | SWT.OK);
mb.setText("No Match"); mb.setMessage("No items match the current filters."); mb.open();
return;
}
var result = applyToView(existingView, elements, relationships, inclusionFilters, exclusionFilters, currentTargetType, synchronize);
var mb = new MessageBox(page1.getShell(), SWT.ICON_INFORMATION | SWT.OK);
mb.setText(existingView ? "View Updated" : "View Created");
mb.setMessage("Added: " + result.added + " Removed: " + result.removed);
mb.open();
}
});
}
// ═══════════════════════════════════════════════════════════════════════════════
// TAB 2 – MANAGE VIEWS (batch update)
//
// Each view is rendered as one row of real SWT widgets as native SWT widgets:
//
// [ ☐ ] View name ………………………… Criteria ………………………………… [ ☑ Sync ]
// ↑ ↑
// select checkbox sync checkbox
// (default checked)
// ═══════════════════════════════════════════════════════════════════════════════
function buildTab2(tabFolder) {
var tab2 = new TabItem(tabFolder, SWT.NONE);
tab2.setText("Manage Views");
var page2 = new Composite(tabFolder, SWT.NONE);
tab2.setControl(page2);
var pl = new GridLayout(1, false); pl.marginWidth = 8; pl.marginHeight = 8;
page2.setLayout(pl);
// ── instructions ──
var labelInfo = new Label(page2, SWT.WRAP);
labelInfo.setText("Views with saved filter criteria are listed below.\n" +
"• First column – tick the views you want to update.\n" +
"• Last column – choose Sync or Add-only independently per view (default: Sync).\n" +
" Sync = add new matching items AND remove items that no longer match.\n" +
" Add only = keep everything already in the view, only add new matches.");
labelInfo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
// ── scan model ──
var filteredViews = [];
$("view").each(function(v) {
var c = loadCriteriaFromView(v);
if (c) filteredViews.push({ view: v, criteria: c });
});
console.log("Manage Views – found " + filteredViews.length + " filtered view(s)");
if (filteredViews.length === 0) {
var lbl = new Label(page2, SWT.WRAP);
lbl.setText("No filtered views found. Use the \"Filter Views\" tab to create some.");
lbl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
return;
}
// ── column headers ──
var headerComp = new Composite(page2, SWT.NONE);
var hLayout = new GridLayout(4, false);
hLayout.horizontalSpacing = 6; hLayout.verticalSpacing = 0;
headerComp.setLayout(hLayout);
headerComp.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
var lblH0 = new Label(headerComp, SWT.CENTER); lblH0.setText("Select");
var gdH0 = new GridData(SWT.CENTER, SWT.CENTER, false, false); gdH0.widthHint = 60; lblH0.setLayoutData(gdH0);
var lblH1 = new Label(headerComp, SWT.NONE); lblH1.setText("View Name");
var gdH1 = new GridData(SWT.FILL, SWT.CENTER, false, false); gdH1.widthHint = 260; lblH1.setLayoutData(gdH1);
var lblH2 = new Label(headerComp, SWT.NONE); lblH2.setText("Criteria");
lblH2.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
var lblH3 = new Label(headerComp, SWT.CENTER); lblH3.setText("Mode");
var gdH3 = new GridData(SWT.CENTER, SWT.CENTER, false, false); gdH3.widthHint = 85; lblH3.setLayoutData(gdH3);
// ── separator ──
new Label(page2, SWT.SEPARATOR | SWT.HORIZONTAL).setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
// ── row area ──
var rowsComp = new Composite(page2, SWT.NONE);
var rLayout = new GridLayout(1, false);
rLayout.horizontalSpacing = 0; rLayout.verticalSpacing = 2;
rLayout.marginWidth = 0; rLayout.marginHeight = 0;
rowsComp.setLayout(rLayout);
rowsComp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
// parallel widget arrays
var selectChecks = []; // one CHECK Button per row – "select this view"
var syncChecks = []; // one CHECK Button per row – "synchronize?"
var selCount = 0;
for (var i = 0; i < filteredViews.length; i++) {
var fv = filteredViews[i];
var row = new Composite(rowsComp, SWT.NONE);
var rowL = new GridLayout(4, false);
rowL.horizontalSpacing = 6; rowL.verticalSpacing = 0;
rowL.marginWidth = 2; rowL.marginHeight = 2;
row.setLayout(rowL);
row.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
// ── select ──
var chkSel = new Button(row, SWT.CHECK);
chkSel.setText("");
var gdSel = new GridData(SWT.CENTER, SWT.CENTER, false, false); gdSel.widthHint = 60;
chkSel.setLayoutData(gdSel);
selectChecks.push(chkSel);
// ── name ──
var lblName = new Label(row, SWT.NONE);
lblName.setText(fv.view.name);
var gdName = new GridData(SWT.FILL, SWT.CENTER, false, false); gdName.widthHint = 260;
lblName.setLayoutData(gdName);
lblName.setToolTipText(fv.view.name);
// ── criteria ──
var lblCrit = new Label(row, SWT.NONE);
lblCrit.setText(formatCriteria(fv.criteria));
lblCrit.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
lblCrit.setToolTipText(formatCriteria(fv.criteria));
// ── sync checkbox ── default = checked (Sync)
var chkSync = new Button(row, SWT.CHECK);
chkSync.setText("Sync");
chkSync.setSelection(true);
var gdSync = new GridData(SWT.CENTER, SWT.CENTER, false, false); gdSync.widthHint = 85;
chkSync.setLayoutData(gdSync);
syncChecks.push(chkSync);
// ── listeners (IIFE to capture loop variable) ──
(function(sel, syn) {
sel.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
selCount += sel.getSelection() ? 1 : -1;
selCountLabel.setText(selCount + " view(s) selected");
}
});
syn.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
syn.setText(syn.getSelection() ? "Sync" : "Add only");
}
});
})(chkSel, chkSync);
}
// ── selection counter ──
var selCountLabel = new Label(page2, SWT.NONE);
selCountLabel.setText("0 view(s) selected");
selCountLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
// ── bottom bar ──
var btnBar = new Composite(page2, SWT.NONE);
btnBar.setLayout(new GridLayout(4, false));
btnBar.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
var btnSelAll = new Button(btnBar, SWT.PUSH); btnSelAll.setText("Select All");
var btnDeselAll = new Button(btnBar, SWT.PUSH); btnDeselAll.setText("Deselect All");
new Label(btnBar, SWT.NONE).setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); // spacer
var btnUpdate = new Button(btnBar, SWT.PUSH); btnUpdate.setText("Update Selected Views");
var gdUpd = new GridData(SWT.FILL, SWT.CENTER, false, false); gdUpd.widthHint = 175; btnUpdate.setLayoutData(gdUpd);
btnSelAll.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
for (var i = 0; i < selectChecks.length; i++) selectChecks[i].setSelection(true);
selCount = selectChecks.length;
selCountLabel.setText(selCount + " view(s) selected");
}
});
btnDeselAll.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
for (var i = 0; i < selectChecks.length; i++) selectChecks[i].setSelection(false);
selCount = 0;
selCountLabel.setText("0 view(s) selected");
}
});
// ── Update ──
btnUpdate.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
// collect ticked rows + their individual sync flag
var selected = [];
for (var i = 0; i < selectChecks.length; i++) {
if (selectChecks[i].getSelection()) {
selected.push({
view : filteredViews[i].view,
criteria : filteredViews[i].criteria,
sync : syncChecks[i].getSelection()
});
}
}
if (selected.length === 0) {
var mb = new MessageBox(page2.getShell(), SWT.ICON_WARNING | SWT.OK);
mb.setText("No Selection"); mb.setMessage("Tick at least one view to update."); mb.open();
return;
}
// confirmation – show per-view mode
var confirmMsg = "Update " + selected.length + " view(s)?\n\n";
for (var i = 0; i < selected.length; i++) {
confirmMsg += (selected[i].sync ? "[Sync] " : "[Add only] ") + selected[i].view.name + "\n";
}
var mbConf = new MessageBox(page2.getShell(), SWT.ICON_QUESTION | SWT.YES | SWT.NO);
mbConf.setText("Confirm Update"); mbConf.setMessage(confirmMsg);
if (mbConf.open() !== SWT.YES) return;
// execute
var totalAdded = 0, totalRemoved = 0, errors = 0;
for (var i = 0; i < selected.length; i++) {
try {
var fv = selected[i];
var inc = fv.criteria.inclusion || [];
var exc = fv.criteria.exclusion || [];
var type = fv.criteria.targetType || "element";
var elements = [], relationships = [];
if (type === "element" || type === "both") elements = findElements(inc, exc);
if (type === "relationship" || type === "both") relationships = findRelationships(inc, exc);
var result = applyToView(fv.view, elements, relationships, inc, exc, type, fv.sync);
totalAdded += result.added;
totalRemoved += result.removed;
} catch (err) { console.log("Error updating '" + fv.view.name + "': " + err); errors++; }
}
// report
var msg = "Batch update complete.\n\n" +
"Views updated : " + selected.length + "\n" +
"Items added : " + totalAdded + "\n" +
"Items removed : " + totalRemoved + "\n";
if (errors > 0) msg += "Errors : " + errors + "\n";
var mbRes = new MessageBox(page2.getShell(), SWT.ICON_INFORMATION | SWT.OK);
mbRes.setText("Done"); mbRes.setMessage(msg);
mbRes.open();
}
});
}
// ═══════════════════════════════════════════════════════════════════════════════
// MAIN
// ═══════════════════════════════════════════════════════════════════════════════
function main() {
var display = Display.getCurrent();
var shell = new Shell(display, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.RESIZE);
shell.setText("Filtered Views Manager");
shell.setSize(1100, 740);
var sl = new GridLayout(1, false); sl.marginWidth = 0; sl.marginHeight = 0;
shell.setLayout(sl);
var tabFolder = new TabFolder(shell, SWT.NONE);
tabFolder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
buildTab1(tabFolder);
buildTab2(tabFolder);
shell.open();
while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); }
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment