Last active
February 5, 2026 13:34
-
-
Save romualdrichard/021185373284b30c26a0656932b86ff0 to your computer and use it in GitHub Desktop.
#jarchi Create View from Properties
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * 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