PageRenderTime 165ms CodeModel.GetById 3ms app.highlight 147ms RepoModel.GetById 1ms app.codeStats 1ms

/cwxeditor_src/cwx/editor/gui/dwt/flagtable.d

https://bitbucket.org/k4nagatsuki/cwxeditor
D | 1848 lines | 1713 code | 77 blank | 58 comment | 239 complexity | ec171a91f6617218784a7fa9676b6ac9 MD5 | raw file
   1
   2module cwx.editor.gui.dwt.flagtable;
   3
   4import cwx.summary;
   5import cwx.flag;
   6import cwx.utils;
   7import cwx.usecounter;
   8import cwx.path;
   9import cwx.menu;
  10import cwx.types;
  11import cwx.system;
  12import cwx.card;
  13import cwx.event;
  14
  15import cwx.editor.gui.dwt.dutils;
  16import cwx.editor.gui.dwt.dprops;
  17import cwx.editor.gui.dwt.commons;
  18import cwx.editor.gui.dwt.xmlbytestransfer;
  19import cwx.editor.gui.dwt.absdialog;
  20import cwx.editor.gui.dwt.centerlayout;
  21import cwx.editor.gui.dwt.undo;
  22import cwx.editor.gui.dwt.dmenu;
  23import cwx.editor.gui.dwt.incsearch;
  24
  25import std.array;
  26import std.ascii;
  27import std.conv;
  28import std.string;
  29import std.exception;
  30
  31import org.eclipse.swt.all;
  32
  33import java.lang.all;
  34
  35/// ??????????????
  36/// ??????10?????
  37public class StepEditDialog : AbsDialog {
  38private:
  39	Commons _comm;
  40	Props prop;
  41	Step _step;
  42	FlagDir dir;
  43
  44	Text stepName;
  45	Combo stepInit;
  46	Text[] stepVals;
  47
  48	void refreshWarning() { mixin(S_TRACE);
  49		string[] ws;
  50		if (prop.sys.isSystemVar(stepName.getText())) { mixin(S_TRACE);
  51			ws ~= .tryFormat(prop.msgs.warningSystemVarName, prop.sys.prefixSystemVarName);
  52		}
  53		warning = ws;
  54	}
  55
  56	class ModValue : ModifyListener {
  57	private:
  58		int index;
  59	public:
  60		this(int index) { mixin(S_TRACE);
  61			this.index = index;
  62		}
  63		override void modifyText(ModifyEvent e) { mixin(S_TRACE);
  64			if (index < stepInit.getItemCount()) { mixin(S_TRACE);
  65				stepInit.setItem(index, (cast(Text) e.getSource()).getText());
  66			}
  67		}
  68	}
  69	class Dispose : DisposeListener {
  70		override void widgetDisposed(DisposeEvent e) { mixin(S_TRACE);
  71			_comm.delFlagAndStep.remove(&delStep);
  72			_comm.refScenario.remove(&refScenario);
  73		}
  74	}
  75	void delStep(Flag[] flag, Step[] step) { mixin(S_TRACE);
  76		foreach (s; step) { mixin(S_TRACE);
  77			if (s is _step) { mixin(S_TRACE);
  78				forceCancel();
  79				return;
  80			}
  81		}
  82	}
  83	void refScenario(Summary summ) { mixin(S_TRACE);
  84		forceCancel();
  85	}
  86public:
  87	/// Params:
  88	/// prop = ?????
  89	/// shell = ???????
  90	/// dir = ?????????????????
  91	/// step = ???????????????null?
  92	this (Commons comm, Props prop, Shell shell, FlagDir dir, Step step = null) { mixin(S_TRACE);
  93		super(prop, shell, false, prop.msgs.dlgTitStep, prop.images.step, true, prop.var.stepDlg, true);
  94		_comm = comm;
  95		this.prop = prop;
  96		this.dir = dir;
  97		this._step = step;
  98		enterClose = true;
  99	}
 100
 101	/// Returns: ?????????????
 102	@property
 103	Step step() { mixin(S_TRACE);
 104		return _step;
 105	}
 106	/// ?????????????????
 107	@property
 108	string name() { mixin(S_TRACE);
 109		auto name = FlagDir.validName(stepName.getText());
 110		if (_step.parent) { mixin(S_TRACE);
 111			return name;
 112		} else { mixin(S_TRACE);
 113			return dir.createNewStepName(name, _step ? _step.name : "");
 114		}
 115	}
 116protected:
 117	override void setup(Composite area) { mixin(S_TRACE);
 118		area.setLayout(zeroGridLayout(1));
 119		{ mixin(S_TRACE);
 120			auto comp = new Composite(area, SWT.NULL);
 121			comp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
 122			comp.setLayout(new GridLayout(5, false));
 123
 124			(new Label(comp, SWT.NULL)).setText(prop.msgs.dlgLblStepName);
 125			stepName = new Text(comp, SWT.BORDER);
 126			mod(stepName);
 127			createTextMenu!Text(_comm, prop, stepName, &catchMod);
 128			setGridMinW(stepName, prop.var.etc.flagNameWidth, GridData.FILL_HORIZONTAL);
 129			checker(stepName);
 130			.listener(stepName, SWT.Modify, &refreshWarning);
 131
 132			auto gd = new GridData(GridData.FILL_VERTICAL);
 133			gd.heightHint = 0;
 134			(new Label(comp, SWT.SEPARATOR | SWT.VERTICAL)).setLayoutData(gd);
 135
 136			(new Label(comp, SWT.NULL)).setText(prop.msgs.dlgLblStepInit);
 137			stepInit = new Combo(comp, SWT.READ_ONLY);
 138			mod(stepInit);
 139			stepInit.setVisibleItemCount(prop.var.etc.comboVisibleItemCount);
 140			setGridMinW(stepInit, prop.var.etc.flagInitWidth);
 141		}
 142		(new Label(area, SWT.SEPARATOR | SWT.HORIZONTAL))
 143			.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
 144		{ mixin(S_TRACE);
 145			auto comp = new Composite(area, SWT.NULL);
 146			comp.setLayoutData(new GridData(GridData.FILL_BOTH));
 147
 148			// ???????????????????
 149			Composite valsComp;
 150			int gdc = 0;
 151			for (int i = 0; i < prop.looks.stepMaxCount; i++) { mixin(S_TRACE);
 152				if (i % 5 == 0) { mixin(S_TRACE);
 153					// 1?????5???????????
 154					if (0 < i) { mixin(S_TRACE);
 155						auto gd = new GridData(GridData.FILL_VERTICAL);
 156						gd.heightHint = 0;
 157						(new Label(comp, SWT.SEPARATOR | SWT.VERTICAL)).setLayoutData(gd);
 158						gdc++;
 159					}
 160					valsComp = new Composite(comp, SWT.NULL);
 161					valsComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
 162					valsComp.setLayout(new GridLayout(2, false));
 163					gdc++;
 164				}
 165				(new Label(valsComp, SWT.NULL)).setText(.tryFormat(prop.msgs.dlgLblStep, i));
 166				auto t = new Text(valsComp, SWT.BORDER);
 167				createTextMenu!Text(_comm, prop, t, &catchMod);
 168				stepVals ~= t;
 169				mod(t);
 170				stepVals[i].addModifyListener(new ModValue(i));
 171				stepVals[i].addFocusListener(new class FocusAdapter {
 172					override void focusGained(FocusEvent e) { mixin(S_TRACE);
 173						auto text = cast(Text) e.widget;
 174						text.selectAll();
 175					}
 176				});
 177				setGridMinW(stepVals[i], prop.var.etc.flagValueWidth, GridData.FILL_HORIZONTAL);
 178			}
 179			comp.setLayout(new GridLayout(gdc, false));
 180		}
 181		_comm.delFlagAndStep.add(&delStep);
 182		_comm.refScenario.add(&refScenario);
 183		getShell().addDisposeListener(new Dispose);
 184
 185		ignoreMod = true;
 186		scope (exit) ignoreMod = false;
 187		string[] vals;
 188		if (_step !is null) { mixin(S_TRACE);
 189			stepName.setText(_step.name);
 190			foreach (i, stepVal; stepVals) { mixin(S_TRACE);
 191				if (i < _step.count) { mixin(S_TRACE);
 192					stepVal.setText(_step.getValue(i));
 193				} else { mixin(S_TRACE);
 194					stepVal.setText(.tryFormat(prop.msgs.dlgTxtStep, i));
 195				}
 196				vals ~= stepVal.getText();
 197			}
 198		} else { mixin(S_TRACE);
 199			stepName.setText("");
 200			foreach (i, stepVal; stepVals) { mixin(S_TRACE);
 201				stepVal.setText(.tryFormat(prop.msgs.dlgTxtStep, i));
 202				vals ~= stepVal.getText();
 203			}
 204		}
 205		if (!_step.parent) { mixin(S_TRACE);
 206			// ?????
 207			stepName.setText("");
 208		}
 209		stepName.selectAll();
 210		setComboItems(stepInit, vals);
 211		stepInit.select(_step is null ? 0 : _step.select);
 212	}
 213
 214	override bool apply() { mixin(S_TRACE);
 215		string[] vals;
 216		foreach (stepVal; stepVals) { mixin(S_TRACE);
 217			vals ~= stepVal.getText();
 218		}
 219		if (_step.parent) { mixin(S_TRACE);
 220			_step.name = this.name;
 221			_step.setValues(vals, stepInit.getSelectionIndex());
 222		} else { mixin(S_TRACE);
 223			_step = new Step(this.name, vals, stepInit.getSelectionIndex());
 224			dir.add(_step);
 225		}
 226		_comm.refFlagAndStep.call([], [_step]);
 227		return true;
 228	}
 229}
 230
 231/// ?????????????
 232public class FlagEditDialog : AbsDialog {
 233private:
 234	Commons _comm;
 235	Props prop;
 236	Flag _flag;
 237	FlagDir dir;
 238
 239	Text flagName;
 240	Combo flagInit;
 241	Combo flagTrue;
 242	Combo flagFalse;
 243
 244	void refreshWarning() { mixin(S_TRACE);
 245		string[] ws;
 246		if (prop.sys.isSystemVar(flagName.getText())) { mixin(S_TRACE);
 247			ws ~= .tryFormat(prop.msgs.warningSystemVarName, prop.sys.prefixSystemVarName);
 248		}
 249		warning = ws;
 250	}
 251
 252	class ModOnOff : SelectionAdapter, ModifyListener {
 253	private:
 254		int index;
 255		void change(E)(E e) { mixin(S_TRACE);
 256			if (index < flagInit.getItemCount()) { mixin(S_TRACE);
 257				flagInit.setItem(index, (cast(Combo) e.getSource()).getText());
 258			}
 259		}
 260	public:
 261		this(int index) { mixin(S_TRACE);
 262			this.index = index;
 263		}
 264		override void widgetSelected(SelectionEvent e) { mixin(S_TRACE);
 265			change(e);
 266		}
 267		override void modifyText(ModifyEvent e) { mixin(S_TRACE);
 268			change(e);
 269		}
 270	}
 271	class Dispose : DisposeListener {
 272		override void widgetDisposed(DisposeEvent e) { mixin(S_TRACE);
 273			_comm.delFlagAndStep.remove(&delFlag);
 274			_comm.refScenario.remove(&refScenario);
 275		}
 276	}
 277	void delFlag(Flag[] flag, Step[] step) { mixin(S_TRACE);
 278		foreach (f; flag) { mixin(S_TRACE);
 279			if (f is _flag) { mixin(S_TRACE);
 280				forceCancel();
 281				return;
 282			}
 283		}
 284	}
 285	void refScenario(Summary summ) { mixin(S_TRACE);
 286		forceCancel();
 287	}
 288public:
 289	/// Params:
 290	/// prop = ?????
 291	/// shell = ???????
 292	/// dir = ????????????????
 293	/// flag = ??????????????null?
 294	this(Commons comm, Props prop, Shell shell, FlagDir dir, Flag flag = null) { mixin(S_TRACE);
 295		super(prop, shell, false, prop.msgs.dlgTitFlag, prop.images.flag, true, prop.var.flagDlg, true);
 296		_comm = comm;
 297		this.prop = prop;
 298		this._flag = flag;
 299		this.dir = dir;
 300		enterClose = true;
 301	}
 302
 303	/// Returns: ????????????
 304	@property
 305	Flag flag() { mixin(S_TRACE);
 306		return _flag;
 307	}
 308	/// ?????????????????
 309	@property
 310	string name() { mixin(S_TRACE);
 311		auto name = FlagDir.validName(flagName.getText());
 312		if (_flag.parent) { mixin(S_TRACE);
 313			return name;
 314		} else { mixin(S_TRACE);
 315			return dir.createNewFlagName(name, _flag ? _flag.name : "");
 316		}
 317	}
 318protected:
 319	private static void setMinW(Control c, int minW, int gridStyle = SWT.NULL) { mixin(S_TRACE);
 320		auto gd = new GridData(gridStyle);
 321		int w = c.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
 322		gd.widthHint = w > minW ? w : minW;
 323		c.setLayoutData(gd);
 324	}
 325
 326	override void setup(Composite area) { mixin(S_TRACE);
 327		area.setLayout(zeroGridLayout(1));
 328		{ mixin(S_TRACE);
 329			auto comp = new Composite(area, SWT.NULL);
 330			comp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
 331			comp.setLayout(new GridLayout(5, false));
 332
 333			(new Label(comp, SWT.NULL)).setText(prop.msgs.dlgLblFlagName);
 334			flagName = new Text(comp, SWT.BORDER);
 335			createTextMenu!Text(_comm, prop, flagName, &catchMod);
 336			mod(flagName);
 337			setGridMinW(flagName, prop.var.etc.flagNameWidth, GridData.FILL_HORIZONTAL);
 338			checker(flagName);
 339			.listener(flagName, SWT.Modify, &refreshWarning);
 340
 341			auto gd = new GridData(GridData.FILL_VERTICAL);
 342			gd.heightHint = 0;
 343			(new Label(comp, SWT.SEPARATOR | SWT.VERTICAL)).setLayoutData(gd);
 344
 345			(new Label(comp, SWT.NULL)).setText(prop.msgs.dlgLblFlagInit);
 346			flagInit = new Combo(comp, SWT.READ_ONLY);
 347			mod(flagInit);
 348			setGridMinW(flagInit, prop.var.etc.flagInitWidth);
 349		}
 350		(new Label(area, SWT.SEPARATOR | SWT.HORIZONTAL))
 351			.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
 352		{ mixin(S_TRACE);
 353			auto ocomp = new Composite(area, SWT.NONE);
 354			ocomp.setLayoutData(new GridData(GridData.FILL_BOTH));
 355			auto cl = new CenterLayout(SWT.HORIZONTAL | SWT.VERTICAL, 0);
 356			cl.fillHorizontal = true;
 357			ocomp.setLayout(cl);
 358			auto comp = new Composite(ocomp, SWT.NONE);
 359			comp.setLayout(new GridLayout(2, false));
 360
 361			(new Label(comp, SWT.NULL)).setText(prop.msgs.dlgLblFlagTrue);
 362			flagTrue = new Combo(comp, SWT.NULL);
 363			mod(flagTrue);
 364			setComboItems(flagTrue, prop.var.etc.flagTrues.dup);
 365			flagTrue.setVisibleItemCount(prop.var.etc.comboVisibleItemCount);
 366			createTextMenu!Combo(_comm, prop, flagTrue, &catchMod);
 367			auto tmod = new ModOnOff(0);
 368			flagTrue.addModifyListener(tmod);
 369			flagTrue.addSelectionListener(tmod);
 370			setGridMinW(flagTrue, prop.var.etc.flagValueWidth, GridData.FILL_HORIZONTAL);
 371
 372			(new Label(comp, SWT.NULL)).setText(prop.msgs.dlgLblFlagFalse);
 373			flagFalse = new Combo(comp, SWT.NULL);
 374			mod(flagFalse);
 375			setComboItems(flagFalse, prop.var.etc.flagFalses.dup);
 376			flagFalse.setVisibleItemCount(prop.var.etc.comboVisibleItemCount);
 377			createTextMenu!Combo(_comm, prop, flagFalse, &catchMod);
 378			auto fmod = new ModOnOff(1);
 379			flagFalse.addModifyListener(fmod);
 380			flagFalse.addSelectionListener(fmod);
 381			setGridMinW(flagFalse, prop.var.etc.flagValueWidth, GridData.FILL_HORIZONTAL);
 382		}
 383		_comm.delFlagAndStep.add(&delFlag);
 384		_comm.refScenario.add(&refScenario);
 385		getShell().addDisposeListener(new Dispose);
 386
 387		ignoreMod = true;
 388		scope (exit) ignoreMod = false;
 389		if (_flag !is null) { mixin(S_TRACE);
 390			flagName.setText(_flag.name);
 391			flagTrue.setText(_flag.on);
 392			if (-1 == flagTrue.indexOf(_flag.on)) flagTrue.add(_flag.on, 0);
 393			flagFalse.setText(_flag.off);
 394			if (-1 == flagFalse.indexOf(_flag.off)) flagFalse.add(_flag.off, 0);
 395			setComboItems(flagInit, [flagTrue.getText(), flagFalse.getText()]);
 396			flagInit.select(_flag.onOff ? 0 : 1);
 397		} else { mixin(S_TRACE);
 398			flagName.setText("");
 399			flagTrue.setText(prop.var.etc.flagTrues.length > 0 ? prop.var.etc.flagTrues[0] : "");
 400			flagFalse.setText(prop.var.etc.flagFalses.length > 0 ? prop.var.etc.flagFalses[0] : "");
 401			setComboItems(flagInit, [flagTrue.getText(), flagFalse.getText()]);
 402			flagInit.select(0);
 403		}
 404		if (!_flag.parent) { mixin(S_TRACE);
 405			// ?????
 406			flagName.setText("");
 407		}
 408		flagName.selectAll();
 409	}
 410
 411	override bool apply() { mixin(S_TRACE);
 412		if (_flag.parent) { mixin(S_TRACE);
 413			_flag.name = this.name;
 414			_flag.onOff = flagInit.getSelectionIndex() == 0;
 415			_flag.on = flagTrue.getText();
 416			_flag.off = flagFalse.getText();
 417		} else { mixin(S_TRACE);
 418			_flag = new Flag(this.name, flagTrue.getText(), flagFalse.getText(),
 419				flagInit.getSelectionIndex() == 0);
 420			dir.add(_flag);
 421		}
 422		_comm.refFlagAndStep.call([_flag], []);
 423		return true;
 424	}
 425}
 426
 427private abstract class FTVUndo : Undo {
 428	protected FlagTable _v;
 429	protected Commons comm;
 430	protected string _dir;
 431	private string _selectedDir;
 432	private string[] _selectedF;
 433	private string[] _selectedS;
 434	private string[] _selectedFB;
 435	private string[] _selectedSB;
 436	protected FlagDir selDir = null;
 437	this (FlagTable v, Commons comm, FlagDir dir) { mixin(S_TRACE);
 438		_v = v;
 439		_dir = dir.path;
 440		this.comm = comm;
 441		saveSelected(v);
 442	}
 443	@property
 444	protected FlagDir dir() { mixin(S_TRACE);
 445		return cast(FlagDir) comm.summary.flagDirRoot.findPath(_dir, false);
 446	}
 447	private void saveSelected(FlagTable v) { mixin(S_TRACE);
 448		auto dir = this.dir();
 449		if (!dir) return;
 450		_selectedDir = dir.cwxPath(true);
 451		if (v && v.flags && !v.flags.isDisposed()) { mixin(S_TRACE);
 452			_selectedF = v.selectionFlagNames;
 453			_selectedS = v.selectionStepNames;
 454		} else { mixin(S_TRACE);
 455			_selectedF.length = 0;
 456			_selectedS.length = 0;
 457		}
 458	}
 459	void udb(FlagTable v) { mixin(S_TRACE);
 460		_selectedFB = _selectedF.dup;
 461		_selectedSB = _selectedS.dup;
 462		saveSelected(v);
 463		if (v && v.flags && !v.flags.isDisposed()) { mixin(S_TRACE);
 464			.forceFocus(v.flags, false);
 465		}
 466	}
 467	void uda(FlagTable v) { mixin(S_TRACE);
 468		if (v && v.flags && !v.flags.isDisposed()) { mixin(S_TRACE);
 469			if (selDir) { mixin(S_TRACE);
 470				if (comm.openCWXPath(selDir.cwxPath(true), true)) { mixin(S_TRACE);
 471					v.flags.deselectAll();
 472				}
 473			} else { mixin(S_TRACE);
 474				if (comm.openCWXPath(_selectedDir, true)) { mixin(S_TRACE);
 475					v.flags.deselectAll();
 476					v.selectFlagNames(_selectedFB);
 477					v.selectStepNames(_selectedSB);
 478				}
 479			}
 480			v.refreshStatusLine();
 481		}
 482		selDir = null;
 483		comm.refreshToolBar();
 484	}
 485	FlagTable view() { mixin(S_TRACE);
 486		return _v;
 487	}
 488	abstract override void undo();
 489	abstract override void redo();
 490	abstract override void dispose();
 491}
 492package class UndoAllVariables : FTVUndo {
 493	private FlagDir _root;
 494	private FlagDir _copyRoot;
 495	this (FlagTable v, Commons comm, FlagDir dir, FlagDir root) { mixin(S_TRACE);
 496		super (v, comm, dir);
 497		_root = root;
 498		_copyRoot = new FlagDir(root);
 499	}
 500	private void impl() { mixin(S_TRACE);
 501		auto v = view();
 502		udb(v);
 503		scope (exit) uda(v);
 504		auto copy = _copyRoot;
 505		_copyRoot = new FlagDir(_root);
 506
 507		_root.removeAll();
 508		foreach (f; copy.flags) _root.add(f);
 509		foreach (f; copy.steps) _root.add(f);
 510		foreach (f; copy.subDirs) _root.add(f);
 511		comm.refFlagAndStep.call(_root.allFlags, _root.allSteps);
 512		comm.openFlagWin(false).dirs.refresh();
 513	}
 514	override void undo() {impl();}
 515	override void redo() {impl();}
 516	override void dispose() {}
 517}
 518package class UndoEditN {
 519	private CWXPath _f;
 520	private string _name;
 521	this (FlagDir dir, int index, string oldName, int value) { mixin(S_TRACE);
 522		auto p = FlagTable.fromIndex(dir, index);
 523		auto f = cast(Flag)p;
 524		if (f) { mixin(S_TRACE);
 525			auto flag = new Flag(f);
 526			_name = flag.name;
 527			flag.name = oldName;
 528			if (-1 != value) flag.onOff = value == 0;
 529			_f = flag;
 530		}
 531		auto s = cast(Step)p;
 532		if (s) { mixin(S_TRACE);
 533			auto step = new Step(s);
 534			_name = step.name;
 535			step.name = oldName;
 536			if (-1 != value) step.select = value;
 537			_f = step;
 538		}
 539	}
 540	CWXPath impl(Commons comm, FlagDir dir, out string newName) { mixin(S_TRACE);
 541		assert (dir);
 542		auto fB = _f;
 543		if (cast(Flag)fB) { mixin(S_TRACE);
 544			auto f = dir.getFlag(_name);
 545			_f = new Flag(f);
 546			auto o = cast(Flag)fB;
 547			assert (o);
 548			_name = o.name;
 549			bool refVal = o.on != f.on || o.off != f.off;
 550			newName = o.name;
 551			o.name = f.name;
 552			f.copyFrom(o);
 553			refVal |= newName != f.name;
 554			if (refVal) { mixin(S_TRACE);
 555				return f;
 556			}
 557		} else { mixin(S_TRACE);
 558			assert (cast(Step)fB);
 559			auto s = dir.getStep(_name);
 560			_f = new Step(s);
 561			auto o = cast(Step)fB;
 562			assert (o);
 563			_name = o.name;
 564			bool refVal = o.values != s.values;
 565			newName = o.name;
 566			o.name = s.name;
 567			s.copyFrom(o);
 568			refVal |= newName != s.name;
 569			if (refVal) { mixin(S_TRACE);
 570				return s;
 571			}
 572		}
 573		return null;
 574	}
 575}
 576package class UndoEdit : FTVUndo {
 577	private UndoEditN[] _impl;
 578	this (FlagTable v, Commons comm, FlagDir dir, int[] index, string[] oldName, int[] oldValues) in { mixin(S_TRACE);
 579		assert (index.length == oldName.length);
 580		assert (!oldValues.length || oldValues.length == index.length);
 581	} body { mixin(S_TRACE);
 582		super (v, comm, dir);
 583		foreach (i, idx; index) { mixin(S_TRACE);
 584			_impl ~= new UndoEditN(dir, idx, oldName[i], oldValues.length ? oldValues[i] : -1);
 585		}
 586	}
 587	private void impl() { mixin(S_TRACE);
 588		auto v = view();
 589		udb(v);
 590		scope (exit) uda(v);
 591		auto dir = this.dir();
 592		Flag[] refF;
 593		Step[] refS;
 594		string[] newNameF;
 595		string[] newNameS;
 596		foreach (impl; _impl) { mixin(S_TRACE);
 597			string newName;
 598			auto refVal = impl.impl(comm, dir, newName);
 599			if (cast(Flag)refVal) { mixin(S_TRACE);
 600				refF ~= cast(Flag)refVal;
 601				newNameF ~= newName;
 602			} else if (cast(Step)refVal) { mixin(S_TRACE);
 603				refS ~= cast(Step)refVal;
 604				newNameS ~= newName;
 605			}
 606		}
 607		FlagTable.setNames(refF, newNameF, comm.summary.useCounter);
 608		FlagTable.setNames(refS, newNameS, comm.summary.useCounter);
 609		comm.refFlagAndStep.call(refF, refS);
 610		if (v && v.flags && !v.flags.isDisposed()) { mixin(S_TRACE);
 611			v.refresh();
 612		}
 613	}
 614	override void undo() {impl();}
 615	override void redo() {impl();}
 616	override void dispose() {}
 617}
 618package class UndoInsertDelete : FTVUndo {
 619	private bool _insert;
 620
 621	/// insert
 622	private int[] _dirIndices;
 623	private string[] _flagName;
 624	private string[] _stepName;
 625	/// delete
 626	private FlagDir[int] _ds;
 627	private Flag[] _fs;
 628	private Step[] _ss;
 629
 630	/// ????????
 631	this (FlagTable v, Commons comm, FlagDir dir, string[] selectedF, string[] selectedS, int[] dirIndices, string[] flagName, string[] stepName) { mixin(S_TRACE);
 632		super (v, comm, dir);
 633		_selectedF = selectedF.dup;
 634		_selectedS = selectedS.dup;
 635		_dirIndices = dirIndices.dup;
 636		_flagName = flagName.dup;
 637		_stepName = stepName.dup;
 638		_insert = true;
 639	}
 640	/// ????????
 641	this (FlagTable v, Commons comm, FlagDir dir, string[] selectedF, string[] selectedS, FlagDir[int] ds, Flag[] fs, Step[] ss) { mixin(S_TRACE);
 642		super (v, comm, dir);
 643		_selectedF = selectedF.dup;
 644		_selectedS = selectedS.dup;
 645		save(ds, fs, ss);
 646		_insert = false;
 647	}
 648	private void save(FlagDir[int] ds, Flag[] fs, Step[] ss) { mixin(S_TRACE);
 649		_ds = ds;
 650		foreach (index, d; _ds) { mixin(S_TRACE);
 651			_ds[index] = new FlagDir(d);
 652		}
 653		_fs.length = 0;
 654		foreach (f; fs) { mixin(S_TRACE);
 655			_fs ~= new Flag(f);
 656		}
 657		_ss.length = 0;
 658		foreach (s; ss) { mixin(S_TRACE);
 659			_ss ~= new Step(s);
 660		}
 661	}
 662	private void undoInsert(FlagTable v) { mixin(S_TRACE);
 663		_insert = false;
 664		FlagDir[int] ds;
 665		Flag[] fs;
 666		Step[] ss;
 667		auto dir = this.dir();
 668		foreach (i; _dirIndices) { mixin(S_TRACE);
 669			auto d = dir.subDirs[i];
 670			if (d) ds[i] = d;
 671		}
 672		foreach (n; _flagName) { mixin(S_TRACE);
 673			auto f = dir.getFlag(n);
 674			if (f) fs ~= f;
 675		}
 676		foreach (n; _stepName) { mixin(S_TRACE);
 677			auto s = dir.getStep(n);
 678			if (s) ss ~= s;
 679		}
 680		save(ds, fs, ss);
 681		foreach (f; fs) { mixin(S_TRACE);
 682			dir.remove(f);
 683		}
 684		foreach (s; ss) { mixin(S_TRACE);
 685			dir.remove(s);
 686		}
 687		foreach (d; ds) { mixin(S_TRACE);
 688			dir.remove(d);
 689			fs ~= d.allFlags;
 690			ss ~= d.allSteps;
 691		}
 692		if (v && v.flags && !v.flags.isDisposed()) { mixin(S_TRACE);
 693			v.refresh();
 694		}
 695		if (ds.length) comm.delFlagDir.call(ds.values);
 696		if (fs.length || ss.length) comm.delFlagAndStep.call(fs, ss);
 697	}
 698	private void undoDelete(FlagTable v) { mixin(S_TRACE);
 699		_insert = true;
 700		auto dir = this.dir();
 701		_dirIndices.length = 0;
 702		_flagName.length = 0;
 703		_stepName.length = 0;
 704		selDir = (_ds.length == 1 && !_fs.length && !_ss.length) ? _ds.values[0] : null;
 705		foreach (f; _fs) { mixin(S_TRACE);
 706			_flagName ~= f.name;
 707			dir.add(f);
 708		}
 709		foreach (s; _ss) { mixin(S_TRACE);
 710			_stepName ~= s.name;
 711			dir.add(s);
 712		}
 713		foreach (index; _ds.keys.sort) { mixin(S_TRACE);
 714			auto d = _ds[index];
 715			_dirIndices ~= index;
 716			dir.insert(index, d);
 717			_fs ~= d.allFlags;
 718			_ss ~= d.allSteps;
 719		}
 720		if (v && v.flags && !v.flags.isDisposed()) { mixin(S_TRACE);
 721			v.refresh();
 722		}
 723		if (_ds.length) comm.refFlagDir.call(_ds.values);
 724		if (_fs.length || _ss.length) comm.refFlagAndStep.call(_fs, _ss);
 725	}
 726	override void undo() { mixin(S_TRACE);
 727		auto v = view();
 728		udb(v);
 729		scope (exit) uda(v);
 730		undoImpl(v);
 731	}
 732	private void undoImpl(FlagTable v) { mixin(S_TRACE);
 733		if (_insert) { mixin(S_TRACE);
 734			undoInsert(v);
 735		} else { mixin(S_TRACE);
 736			undoDelete(v);
 737		}
 738	}
 739	override void redo() { mixin(S_TRACE);
 740		auto v = view();
 741		udb(v);
 742		scope (exit) uda(v);
 743		redoImpl(v);
 744	}
 745	private void redoImpl(FlagTable v) { mixin(S_TRACE);
 746		undoImpl(v);
 747	}
 748	override void dispose() {}
 749}
 750package class UndoMove : FTVUndo {
 751	private UndoInsertDelete _dir1;
 752	private UndoInsertDelete _dir2;
 753	private string[string] _cFlags;
 754	private string[string] _cSteps;
 755
 756	this (FlagTable v, Commons comm, string[] selectedF, string[] selectedS, FlagDir to, int[] dirIndices, string[] flagName, string[] stepName, FlagDir from, FlagDir[int] ds, Flag[] fs, Step[] ss, Flag[string] cFlags, Step[string] cSteps) { mixin(S_TRACE);
 757		super (v, comm, from);
 758		_selectedF = selectedF.dup;
 759		_selectedS = selectedS.dup;
 760		assert (dirIndices.length == ds.length);
 761		assert (flagName.length == fs.length);
 762		assert (stepName.length == ss.length);
 763		_dir1 = new UndoInsertDelete(v, comm, to, selectedF, selectedS, dirIndices, flagName, stepName);
 764		_dir2 = new UndoInsertDelete(v, comm, from, selectedF, selectedS, ds, fs, ss);
 765		foreach (oPath, flag; cFlags) { mixin(S_TRACE);
 766			_cFlags[oPath] = flag.path;
 767		}
 768		foreach (oPath, step; cSteps) { mixin(S_TRACE);
 769			_cSteps[oPath] = step.path;
 770		}
 771	}
 772	private void change(FlagTable v) { mixin(S_TRACE);
 773		string[string] cFlags;
 774		string[string] cSteps;
 775		foreach (nPath, oPath; _cFlags) { mixin(S_TRACE);
 776			cFlags[oPath] = nPath;
 777			comm.summary.useCounter.change(toFlagId(oPath), toFlagId(nPath));
 778		}
 779		foreach (nPath, oPath; _cSteps) { mixin(S_TRACE);
 780			cSteps[oPath] = nPath;
 781			comm.summary.useCounter.change(toStepId(oPath), toStepId(nPath));
 782		}
 783		_cFlags = cFlags;
 784		_cSteps = cSteps;
 785		if (v && v.flags && !v.flags.isDisposed()) { mixin(S_TRACE);
 786			v.__refreshUseCount();
 787		}
 788	}
 789	override void undo() { mixin(S_TRACE);
 790		auto v = view();
 791		udb(v);
 792		scope (exit) uda(v);
 793		_dir1.undoImpl(v);
 794		_dir2.undoImpl(v);
 795		change(v);
 796	}
 797	override void redo() { mixin(S_TRACE);
 798		auto v = view();
 799		udb(v);
 800		scope (exit) uda(v);
 801		_dir2.redoImpl(v);
 802		_dir1.redoImpl(v);
 803		change(v);
 804	}
 805	override void dispose() { mixin(S_TRACE);
 806		_dir1.dispose();
 807		_dir2.dispose();
 808	}
 809}
 810package class UndoEditDir : FTVUndo {
 811	private string _oldName;
 812	this (FlagTable v, Commons comm, FlagDir dir, string oldName) { mixin(S_TRACE);
 813		super (v, comm, dir);
 814		_oldName = oldName;
 815	}
 816	private void impl() { mixin(S_TRACE);
 817		auto v = view();
 818		udb(v);
 819		scope (exit) uda(v);
 820		auto dir = this.dir();
 821
 822		string oldName = dir.name;
 823		dir.rename(_oldName, comm.summary.useCounter);
 824		if (dir.parent) dir.parent.sortSubDirs(false);
 825		_oldName = oldName;
 826
 827		comm.refFlagDir.call([dir]);
 828		_dir = dir.path;
 829	}
 830	override void undo() {impl();}
 831	override void redo() {impl();}
 832	override void dispose() {}
 833}
 834package class UndoSwap : FTVUndo {
 835	private FlagDir _parent;
 836	private int _index1, _index2;
 837	this (FlagTable v, Commons comm, FlagDir dir, FlagDir parent, int index1, int index2) { mixin(S_TRACE);
 838		super (v, comm, dir);
 839		_parent = parent;
 840		_index1 = index1;
 841		_index2 = index2;
 842	}
 843	private void impl() { mixin(S_TRACE);
 844		auto v = view();
 845		udb(v);
 846		scope (exit) uda(v);
 847
 848		_parent.swapDir(_index1, _index2);
 849
 850		comm.refFlagDir.call([_parent, _parent.subDirs[_index1], _parent.subDirs[_index2]]);
 851	}
 852	override void undo() {impl();}
 853	override void redo() {impl();}
 854	override void dispose() {}
 855}
 856
 857public class FlagTable : TCPD {
 858private:
 859	void storeEdit(int[] index, string[] oldName, int[] oldValues = []) { mixin(S_TRACE);
 860		_undo ~= new UndoEdit(this, _comm, _dir, index, oldName, oldValues);
 861	}
 862	void storeEdit(int index, string oldName, int oldValue) { mixin(S_TRACE);
 863		_undo ~= new UndoEdit(this, _comm, _dir, [index], [oldName], [oldValue]);
 864	}
 865	void storeInsert(string[] selectedF, string[] selectedS, string[] flagName, string[] stepName) { mixin(S_TRACE);
 866		_undo ~= new UndoInsertDelete(this, _comm, _dir, selectedF, selectedS, [], flagName, stepName);
 867	}
 868	void storeDelete(string[] selectedF, string[] selectedS, Flag[] fs, Step[] ss) { mixin(S_TRACE);
 869		FlagDir[int] ds;
 870		_undo ~= new UndoInsertDelete(this, _comm, _dir, selectedF, selectedS, ds, fs, ss);
 871	}
 872
 873	static int indexOf(FlagDir dir, CWXPath p) { mixin(S_TRACE);
 874		int i = 0;
 875		foreach (s; dir.steps) { mixin(S_TRACE);
 876			if (s is p) { mixin(S_TRACE);
 877				return i;
 878			}
 879			i++;
 880		}
 881		foreach (f; dir.flags) { mixin(S_TRACE);
 882			if (f is p) { mixin(S_TRACE);
 883				return i;
 884			}
 885			i++;
 886		}
 887		return -1;
 888	}
 889	static CWXPath fromIndex(FlagDir dir, int index) { mixin(S_TRACE);
 890		if (dir.steps.length <= index) { mixin(S_TRACE);
 891			return dir.flags[index - dir.steps.length];
 892		}
 893		return dir.steps[index];
 894	}
 895	static int toFlagIndex(FlagDir dir, int index) { mixin(S_TRACE);
 896		return index - dir.steps.length;
 897	}
 898	static int toStepIndex(FlagDir dir, int index) { mixin(S_TRACE);
 899		return index;
 900	}
 901
 902	static const NAME = 0;
 903	static const VALUE = 1;
 904	static const UC = 2;
 905	void refreshFlags() { mixin(S_TRACE);
 906		if (_dir) { mixin(S_TRACE);
 907			int i = 0;
 908			foreach (f; _dir.steps) { mixin(S_TRACE);
 909				if (!_incSearch.match(f.name, f)) continue;
 910				TableItem itm;
 911				if (i < flags.getItemCount()) { mixin(S_TRACE);
 912					itm = flags.getItem(i);
 913				} else { mixin(S_TRACE);
 914					itm = new TableItem(flags, SWT.NONE);
 915				}
 916				itm.setImage(0, prop.images.step);
 917				itm.setText(NAME, f.name);
 918				itm.setText(VALUE, f.value);
 919				itm.setText(UC, to!(string)(uc.get(toStepId(f.path))));
 920				itm.setData(f);
 921				i++;
 922			}
 923			foreach (f; _dir.flags) { mixin(S_TRACE);
 924				if (!_incSearch.match(f.name, f)) continue;
 925				TableItem itm;
 926				if (i < flags.getItemCount()) { mixin(S_TRACE);
 927					itm = flags.getItem(i);
 928				} else { mixin(S_TRACE);
 929					itm = new TableItem(flags, SWT.NONE);
 930				}
 931				itm.setImage(0, prop.images.flag);
 932				itm.setText(NAME, f.name);
 933				itm.setText(VALUE, f.onOff ? f.on : f.off);
 934				itm.setText(UC, to!(string)(uc.get(toFlagId(f.path))));
 935				itm.setData(f);
 936				i++;
 937			}
 938			if (i < flags.getItemCount()) { mixin(S_TRACE);
 939				flags.remove(i, flags.getItemCount() - 1);
 940			}
 941		} else { mixin(S_TRACE);
 942			flags.removeAll();
 943		}
 944	}
 945
 946	Props prop;
 947	Commons _comm;
 948	UseCounter uc;
 949
 950	Table flags;
 951
 952	FlagDir _dir = null;
 953	string _statusLine = "";
 954
 955	FlagEditDialog[Flag] _editDlgsF;
 956	StepEditDialog[Step] _editDlgsS;
 957
 958	IncSearch _incSearch = null;
 959	private void incSearch() { mixin(S_TRACE);
 960		.forceFocus(flags, true);
 961		_incSearch.startIncSearch();
 962	}
 963
 964	UndoManager _undo;
 965
 966	void editFlag(FlagDir parent, Flag flag) { mixin(S_TRACE);
 967		bool createMode = flag is null;
 968		string old = createMode ? null : flag.path;
 969		if (!flag) { mixin(S_TRACE);
 970			string on = prop.var.etc.flagTrues.length > 0 ? prop.var.etc.flagTrues[0] : "";
 971			string off = prop.var.etc.flagFalses.length > 0 ? prop.var.etc.flagFalses[0] : "";
 972			flag = new Flag("", on, off, true);
 973		}
 974		auto p = flag in _editDlgsF;
 975		if (p) { mixin(S_TRACE);
 976			p.active();
 977			return;
 978		}
 979		auto dlg = new FlagEditDialog(_comm, prop, dlgParShl, parent, flag);
 980		string oldName = "";
 981		int oldValue = 0;
 982		dlg.applyEvent ~= { mixin(S_TRACE);
 983			int i = indexOf(parent, flag);
 984			if (-1 != i) { mixin(S_TRACE);
 985				assert (!createMode);
 986				oldName = flag.name;
 987				oldValue = flag.onOff ? 0 : 1;
 988			}
 989		};
 990		dlg.appliedEvent ~= { mixin(S_TRACE);
 991			auto flag = dlg.flag;
 992			if (old && old != flag.path) uc.change(toFlagId(old), toFlagId(flag.path), true);
 993			if (createMode) { mixin(S_TRACE);
 994				string[] selsF;
 995				string[] selsS;
 996				if (flags && !flags.isDisposed()) { mixin(S_TRACE);
 997					selsF = selectionFlagNames;
 998					selsS = selectionStepNames;
 999				}
1000				storeInsert(selsF, selsS, [flag.name], []);
1001				createMode = false;
1002			} else { mixin(S_TRACE);
1003				storeEdit(indexOf(parent, flag), oldName, oldValue);
1004			}
1005			_comm.openCWXPath(flag.cwxPath(true), false);
1006			refresh(flag);
1007			_comm.refFlagAndStep.call([flag], []);
1008			_comm.refreshToolBar();
1009		};
1010		_editDlgsF[flag] = dlg;
1011		dlg.closeEvent ~= { mixin(S_TRACE);
1012			_editDlgsF.remove(flag);
1013		};
1014		dlg.open();
1015	}
1016	void editStep(FlagDir parent, Step step) { mixin(S_TRACE);
1017		bool createMode = step is null;
1018		string old = createMode ? null : step.path;
1019		if (!step) { mixin(S_TRACE);
1020			string[] vals;
1021			foreach (i; 0 .. prop.looks.stepMaxCount) { mixin(S_TRACE);
1022				vals ~= .tryFormat(prop.msgs.dlgTxtStep, i);
1023			}
1024			step = new Step("", vals, 0);
1025		}
1026		auto p = step in _editDlgsS;
1027		if (p) { mixin(S_TRACE);
1028			p.active();
1029			return;
1030		}
1031		auto dlg = new StepEditDialog(_comm, prop, dlgParShl, parent, step);
1032		string oldName = "";
1033		int oldValue = 0;
1034		dlg.applyEvent ~= { mixin(S_TRACE);
1035			int i = indexOf(parent, step);
1036			if (-1 != i) { mixin(S_TRACE);
1037				assert (!createMode);
1038				oldName = step.name;
1039				oldValue = step.select;
1040			}
1041		};
1042		dlg.appliedEvent ~= { mixin(S_TRACE);
1043			auto step = dlg.step;
1044			if (old && old != step.path) uc.change(toStepId(old), toStepId(step.path), true);
1045			if (createMode) { mixin(S_TRACE);
1046				string[] selsF;
1047				string[] selsS;
1048				if (flags && !flags.isDisposed()) { mixin(S_TRACE);
1049					selsF = selectionFlagNames;
1050					selsS = selectionStepNames;
1051				}
1052				storeInsert(selsF, selsS, [step.name], []);
1053				createMode = false;
1054			} else { mixin(S_TRACE);
1055				storeEdit(indexOf(parent, step), oldName, oldValue);
1056			}
1057			_comm.openCWXPath(step.cwxPath(true), false);
1058			refresh(step);
1059			_comm.refFlagAndStep.call([], [step]);
1060			_comm.refreshToolBar();
1061		};
1062		_editDlgsS[step] = dlg;
1063		dlg.closeEvent ~= { mixin(S_TRACE);
1064			_editDlgsS.remove(step);
1065		};
1066		dlg.open();
1067	}
1068
1069	class MListener : MouseAdapter {
1070	public:
1071		override void mouseDoubleClick(MouseEvent e) { mixin(S_TRACE);
1072			if (e.button == 1) { mixin(S_TRACE);
1073				edit();
1074			}
1075		}
1076	}
1077	class KListener : KeyAdapter {
1078	public:
1079		override void keyPressed(KeyEvent e) { mixin(S_TRACE);
1080			if (e.character == SWT.CR) { mixin(S_TRACE);
1081				edit();
1082			}
1083		}
1084	}
1085
1086	/// ??????????????????
1087	/// Params:
1088	/// fs = ?????????????????
1089	/// ss = ??????????????????
1090	/// Returns: ?????true?
1091	bool getSelectionFlagAndStep(out Flag[] fs, out Step[] ss) { mixin(S_TRACE);
1092		auto indices = flags.getSelectionIndices();
1093		if (indices.length > 0) { mixin(S_TRACE);
1094			foreach (i; indices) { mixin(S_TRACE);
1095				auto itm = flags.getItem(i);
1096				if (auto s = cast(Step)itm.getData()) { mixin(S_TRACE);
1097					ss ~= s;
1098				} else if (auto f = cast(Flag)itm.getData()) { mixin(S_TRACE);
1099					fs ~= f;
1100				} else assert (0);
1101			}
1102			return true;
1103		} else { mixin(S_TRACE);
1104			return false;
1105		}
1106	}
1107	void selectNamesImpl(F)(string[] names) { mixin(S_TRACE);
1108		auto set = new HashSet!string;
1109		foreach (name; names) set.add(name.toLower());
1110		foreach (i, itm; flags.getItems()) { mixin(S_TRACE);
1111			if (auto f = cast(F)itm.getData()) { mixin(S_TRACE);
1112				if (set.contains(f.name.toLower())) { mixin(S_TRACE);
1113					flags.select(i);
1114				}
1115			}
1116		}
1117	}
1118	void selectFlagNames(string[] names) { mixin(S_TRACE);
1119		selectNamesImpl!Flag(names);
1120	}
1121	void selectStepNames(string[] names) { mixin(S_TRACE);
1122		selectNamesImpl!Step(names);
1123	}
1124
1125	Flag[] _dragFlags;
1126	Step[] _dragSteps;
1127	class FlagDragListener : DragSourceListener {
1128	private:
1129		int[] dragIndices;
1130	public:
1131		override void dragStart(DragSourceEvent e) { mixin(S_TRACE);
1132			e.doit = flags.getSelectionCount() > 0;
1133		}
1134		override void dragSetData(DragSourceEvent e) { mixin(S_TRACE);
1135			if (XMLBytesTransfer.getInstance().isSupportedType(e.dataType)) { mixin(S_TRACE);
1136				// XML????????
1137				getSelectionFlagAndStep(_dragFlags, _dragSteps);
1138				e.data = bytesFromXML(getXML(_dir, _dragFlags, _dragSteps));
1139			}
1140		}
1141		override void dragFinished(DragSourceEvent e) { mixin(S_TRACE);
1142			if (e.detail == DND.DROP_MOVE) { mixin(S_TRACE);
1143				foreach (flag; _dragFlags) { mixin(S_TRACE);
1144					flag.parent.remove(flag);
1145				}
1146				foreach (step; _dragSteps) { mixin(S_TRACE);
1147					step.parent.remove(step);
1148				}
1149				refresh();
1150				_comm.delFlagAndStep.call(_dragFlags, _dragSteps);
1151				_comm.refreshToolBar();
1152			}
1153			_dragFlags.length = 0;
1154			_dragSteps.length = 0;
1155		}
1156	}
1157	void flagsSelected() { mixin(S_TRACE);
1158		refreshStatusLine();
1159		_comm.refreshToolBar();
1160	}
1161	class SListener : SelectionAdapter {
1162		override void widgetSelected(SelectionEvent e) { mixin(S_TRACE);
1163			flagsSelected();
1164		}
1165	}
1166	class DListener : DisposeListener {
1167		override void widgetDisposed(DisposeEvent e) { mixin(S_TRACE);
1168			_comm.refUseCount.remove(&__refreshUseCount);
1169			_comm.replText.remove(&refresh);
1170		}
1171	}
1172	void nameEditEnd(TableItem selItm, int column, string text) { mixin(S_TRACE);
1173		text = FlagDir.validName(text);
1174		auto itms = flags.getSelection();
1175		itms = itms.remove(selItm);
1176		itms.insertInPlace(0, selItm);
1177		Flag[] refF;
1178		Step[] refS;
1179		int[] indices;
1180		string[] oldNames;
1181		string[] oldNamesF;
1182		string[] oldNamesS;
1183		FlagId[] oldFID;
1184		StepId[] oldSID;
1185		foreach (itm; itms) { mixin(S_TRACE);
1186			auto f = cast(Flag)itm.getData();
1187			if (f) { mixin(S_TRACE);
1188				indices ~= indexOf(f.parent, f);
1189				oldNamesF ~= f.name;
1190				oldNames ~= f.name;
1191				refF ~= f;
1192				oldFID ~= toFlagId(f.path);
1193			}
1194			auto s = cast(Step)itm.getData();
1195			if (s) { mixin(S_TRACE);
1196				indices ~= indexOf(s.parent, s);
1197				oldNamesS ~= s.name;
1198				oldNames ~= s.name;
1199				refS ~= s;
1200				oldSID ~= toStepId(s.path);
1201			}
1202		}
1203		if (indices.length) { mixin(S_TRACE);
1204			auto newNamesF = _dir.createNewFlagNames(text, refF.length, oldNamesF);
1205			auto newNamesS = _dir.createNewStepNames(text, refS.length, oldNamesS);
1206			bool changed = false;
1207			changed |= setNames(refF, newNamesF, uc);
1208			changed |= setNames(refS, newNamesS, uc);
1209			if (changed) { mixin(S_TRACE);
1210				storeEdit(indices, oldNames);
1211				refresh();
1212				_comm.refFlagAndStep.call(refF, refS);
1213			}
1214		}
1215		_comm.refreshToolBar();
1216	}
1217	static bool setNames(F)(F[] refVals, in string[] newNames, UseCounter uc) { mixin(S_TRACE);
1218		// ???????????????????????????
1219		if (!refVals.length) return false;
1220		auto dir = refVals[0].parent;
1221		auto newSet = new HashSet!string;
1222		foreach (name; newNames) newSet.add(name.toLower());
1223		auto tempSet = new HashSet!string;
1224		foreach (i; 0..refVals.length) { mixin(S_TRACE);
1225			auto name = createNewName("temp", (string name) { mixin(S_TRACE);
1226				if (!dir.canAppend!F(name)) return false;
1227				name = name.toLower();
1228				return !newSet.contains(name) && !tempSet.contains(name);
1229			});
1230			tempSet.add(name);
1231		}
1232		string[] oldNames;
1233		foreach (i, tempName; tempSet.toArray()) { mixin(S_TRACE);
1234			auto f = refVals[i];
1235			oldNames ~= f.name;
1236			auto oldID = F.toID(f.path);
1237			f.name = tempName;
1238			uc.change(oldID, F.toID(f.path), false);
1239		}
1240		bool changed = false;
1241		foreach (i, name; newNames) { mixin(S_TRACE);
1242			auto f = refVals[i];
1243			auto oldID = F.toID(f.path);
1244			f.name = name;
1245			uc.change(oldID, F.toID(f.path), false);
1246			if (oldNames[i] != name) changed = true;
1247		}
1248		return changed;
1249	}
1250	void initCombo(TableItem itm, int column, out string[] strs, out string str) { mixin(S_TRACE);
1251		auto f = cast(Flag) itm.getData();
1252		if (f) { mixin(S_TRACE);
1253			strs = [f.on, f.off];
1254			str = f.onOff ? f.on : f.off;
1255			return;
1256		}
1257		auto s = cast(Step) itm.getData();
1258		if (s) { mixin(S_TRACE);
1259			foreach (i, v; s.values) { mixin(S_TRACE);
1260				strs ~= v;
1261				if (i == s.select) { mixin(S_TRACE);
1262					str = v;
1263				}
1264			}
1265			return;
1266		}
1267	}
1268	void initEditEnd(TableItem selItm, int column, Combo combo) { mixin(S_TRACE);
1269		int i = combo.getSelectionIndex();
1270		if (-1 == i) return;
1271		auto selFlag = cast(Flag)selItm.getData();
1272		auto selStep = cast(Step)selItm.getData();
1273		auto itms = flags.getSelection();
1274		itms = itms.remove(selItm);
1275		itms.insertInPlace(0, selItm);
1276		Flag[] refF;
1277		Step[] refS;
1278		int[] indices;
1279		string[] oldNames;
1280		int[] oldValues;
1281		foreach (itm; itms) { mixin(S_TRACE);
1282			auto f = cast(Flag)itm.getData();
1283			if (selFlag && f) { mixin(S_TRACE);
1284				if (f.onOff == (0 == i)) continue;
1285				indices ~= indexOf(f.parent, f);
1286				oldNames ~= f.name;
1287				oldValues ~= f.onOff ? 0 : 1;
1288				f.onOff = 0 == i;
1289				itm.setText(column, f.onOff ? f.on : f.off);
1290				refF ~= f;
1291			}
1292			auto s = cast(Step)itm.getData();
1293			if (selStep && s) { mixin(S_TRACE);
1294				if (s.select == i) continue; 
1295				indices ~= indexOf(s.parent, s);
1296				oldNames ~= s.name;
1297				oldValues ~= s.select;
1298				s.select(i);
1299				itm.setText(column, s.value);
1300				refS ~= s;
1301			}
1302		}
1303		if (indices.length) { mixin(S_TRACE);
1304			storeEdit(indices, oldNames, oldValues);
1305			_comm.refFlagAndStep.call(refF, refS);
1306		}
1307		_comm.refreshToolBar();
1308	}
1309	class Dispose : DisposeListener {
1310		override void widgetDisposed(DisposeEvent e) { mixin(S_TRACE);
1311			foreach (dlg; _editDlgsF.values) { mixin(S_TRACE);
1312				dlg.forceCancel();
1313			}
1314			foreach (dlg; _editDlgsS.values) { mixin(S_TRACE);
1315				dlg.forceCancel();
1316			}
1317		}
1318	}
1319	@property
1320	Shell dlgParShl() { mixin(S_TRACE);
1321		if (flags && !flags.isDisposed()) return flags.getShell();
1322		return _comm.mainWin.shell.getShell();
1323	}
1324	void selectAll() { mixin(S_TRACE);
1325		foreach (i; 0 .. flags.getItemCount()) { mixin(S_TRACE);
1326			flags.select(i);
1327		}
1328		flagsSelected();
1329	}
1330	void copyFlagTree(bool onOff) { mixin(S_TRACE);
1331		auto c = createSetFlagTree(selectionFlags, onOff);
1332		if (!c) return;
1333		XMLtoCB(prop, _comm.clipboard, c.toXML(new XMLOption(prop.sys)));
1334		_comm.refreshToolBar();
1335	}
1336	void copyStepTree(int value) { mixin(S_TRACE);
1337		auto c = createSetStepTree(selectionSteps, value);
1338		if (!c) return;
1339		XMLtoCB(prop, _comm.clipboard, c.toXML(new XMLOption(prop.sys)));
1340		_comm.refreshToolBar();
1341	}
1342	void copyInitTree() { mixin(S_TRACE);
1343		Flag[] fs;
1344		Step[] ss;
1345		getSelectionFlagAndStep(fs, ss);
1346		auto c = createInitVariablesTree(fs, ss);
1347		if (!c) return;
1348		XMLtoCB(prop, _comm.clipboard, c.toXML(new XMLOption(prop.sys)));
1349		_comm.refreshToolBar();
1350	}
1351	void copyFlagReverseTree() { mixin(S_TRACE);
1352		auto c = createReverseFlagTree(selectionFlags);
1353		if (!c) return;
1354		XMLtoCB(prop, _comm.clipboard, c.toXML(new XMLOption(prop.sys)));
1355		_comm.refreshToolBar();
1356	}
1357	void copyStepUpTree() { mixin(S_TRACE);
1358		auto c = createSetStepUpTree(selectionSteps);
1359		if (!c) return;
1360		XMLtoCB(prop, _comm.clipboard, c.toXML(new XMLOption(prop.sys)));
1361		_comm.refreshToolBar();
1362	}
1363	void copyStepDownTree() { mixin(S_TRACE);
1364		auto c = createSetStepDownTree(selectionSteps);
1365		if (!c) return;
1366		XMLtoCB(prop, _comm.clipboard, c.toXML(new XMLOption(prop.sys)));
1367		_comm.refreshToolBar();
1368	}
1369	void copyVariablePath() { mixin(S_TRACE);
1370		if (0 == flags.getSelectionCount()) return;
1371
1372		char[] buf;
1373		foreach (itm; flags.getSelection()) { mixin(S_TRACE);
1374			if (buf.length) buf ~= .newline;
1375			auto f = cast(Flag)itm.getData();
1376			if (f) buf ~= f.path;
1377			auto s = cast(Step)itm.getData();
1378			if (s) buf ~= s.path;
1379			assert (f || s);
1380		}
1381		_comm.clipboard.setContents([new ArrayWrapperString(buf)],
1382			[TextTransfer.getInstance()]);
1383		_comm.refreshToolBar();
1384	}
1385public:
1386	this (Commons comm, Props prop, UndoManager undo) { mixin(S_TRACE);
1387		_undo = undo;
1388		_comm = comm;
1389		this.prop = prop;
1390	}
1391
1392	/// ????????????
1393	/// Params:
1394	/// parent = ????????
1395	Control createControl(Composite parent, Composite incSearchParent) { mixin(S_TRACE);
1396		_comp = new Composite(parent, SWT.NONE);
1397		_comp.setLayout(new FillLayout);
1398		flags = new Table(_comp, SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION);
1399		flags.setHeaderVisible(true);
1400		auto nameCol = new TableColumn(flags, SWT.NULL);
1401		nameCol.setText(prop.msgs.flagName);
1402		saveColumnWidth!("prop.var.etc.flagNameColumn")(prop, nameCol);
1403		auto initCol = new TableColumn(flags, SWT.NULL);
1404		initCol.setText(prop.msgs.flagInit);
1405		saveColumnWidth!("prop.var.etc.flagInitColumn")(prop, initCol);
1406		auto countCol = new TableColumn(flags, SWT.NULL);
1407		countCol.setText(prop.msgs.flagCount);
1408		saveColumnWidth!("prop.var.etc.flagCountColumn")(prop, countCol);
1409
1410		updateIncSearchParent(incSearchParent);
1411
1412		flags.addKeyListener(new KListener);
1413		flags.addMouseListener(new MListener);
1414		auto menu = new Menu(flags.getShell(), SWT.POP_UP);
1415		createMenuItem(_comm, menu, MenuID.IncSearch, &incSearch, null);
1416		new MenuItem(menu, SWT.SEPARATOR);
1417		createMenuItem(_comm, menu, MenuID.EditProp, &edit, &canEdit);
1418		new MenuItem(menu, SWT.SEPARATOR);
1419		createMenuItem(_comm, menu, MenuID.NewFlag, &createFlag, () => _dir !is null);
1420		createMenuItem(_comm, menu, MenuID.NewStep, &createStep, () => _dir !is null);
1421		new MenuItem(menu, SWT.SEPARATOR);
1422		createMenuItem(_comm, menu, MenuID.Undo, &this.undo, &_undo.canUndo);
1423		createMenuItem(_comm, menu, MenuID.Redo, &this.redo, &_undo.canRedo);
1424		new MenuItem(menu, SWT.SEPARATOR);
1425		appendMenuTCPD(_comm, menu, this, true, true, true, true, true);
1426		new MenuItem(menu, SWT.SEPARATOR);
1427		createMenuItem(_comm, menu, MenuID.SelectAll, &selectAll, () => flags.getItemCount() && flags.getSelectionCount() != flags.getItemCount());
1428		new MenuItem(menu, SWT.SEPARATOR);
1429
1430		void delegate() dlg = null;
1431		auto evt = createMenuItem(_comm, menu, MenuID.CreateVariableEventTree, dlg, () => 0 < flags.getSelectionCount(), SWT.CASCADE);
1432		auto mEvt = new Menu(parent.getShell(), SWT.DROP_DOWN);
1433		evt.setMenu(mEvt);
1434		createMenuItem(_comm, mEvt, MenuID.InitVariablesTree, &copyInitTree, () => 0 < flags.getSelectionCount());
1435		new MenuItem(mEvt, SWT.SEPARATOR);
1436		createMenuItem2(_comm, mEvt, MenuProps.buildMenu(prop.msgs.contentName(CType.REVERSE_FLAG), "R", "", false), prop.images.content(CType.REVERSE_FLAG), &copyFlagReverseTree, () => 0 < selectionFlags.length);
1437		new MenuItem(mEvt, SWT.SEPARATOR);
1438		createMenuItem2(_comm, mEvt, MenuProps.buildMenu(prop.msgs.setFlagTrue, "T", "", false), prop.images.content(CType.SET_FLAG), () => copyFlagTree(true), () => 0 < selectionFlags.length);
1439		createMenuItem2(_comm, mEvt, MenuProps.buildMenu(prop.msgs.setFlagFalse, "F", "", false), prop.images.content(CType.SET_FLAG), () => copyFlagTree(false), () => 0 < selectionFlags.length);
1440		new MenuItem(mEvt, SWT.SEPARATOR);
1441		createMenuItem2(_comm, mEvt, MenuProps.buildMenu(prop.msgs.contentName(CType.SET_STEP_UP), "U", "", false), prop.images.content(CType.SET_STEP_UP), &copyStepUpTree, () => 0 < selectionSteps.length);
1442		createMenuItem2(_comm, mEvt, MenuProps.buildMenu(prop.msgs.contentName(CType.SET_STEP_DOWN), "D", "", false), prop.images.content(CType.SET_STEP_DOWN), &copyStepDownTree, () => 0 < selectionSteps.length);
1443		new MenuItem(mEvt, SWT.SEPARATOR);
1444		void ssValue(uint i) { mixin(S_TRACE);
1445			string mnemonic = i < 10 ? .text(i) : "";
1446			createMenuItem2(_comm, mEvt, MenuProps.buildMenu(.tryFormat(prop.msgs.setStepValue, .tryFormat(prop.msgs.dlgTxtStep, i)), mnemonic, "", false), prop.images.content(CType.SET_STEP), () => copyStepTree(i), () => 0 < selectionSteps.length);
1447		}
1448		foreach (i; 0..prop.looks.stepMaxCount) { mixin(S_TRACE);
1449			ssValue(i);
1450		}
1451
1452		new MenuItem(menu, SWT.SEPARATOR);
1453		createMenuItem(_comm, menu, MenuID.FindID, &replaceID, &canReplaceID);
1454		new MenuItem(menu, SWT.SEPARATOR);
1455		createMenuItem(_comm, menu, MenuID.CopyVariablePath, &copyVariablePath, () => 0 < flags.getSelectionCount());
1456		flags.setMenu(menu);
1457
1458		auto ds = new DragSource(flags, DND.DROP_MOVE | DND.DROP_COPY);
1459		ds.setTransfer([XMLBytesTransfer.getInstance()]);
1460		ds.addDragListener(new FlagDragListener);
1461
1462		_comm.refUseCount.add(&__refreshUseCount);
1463		_comm.replText.add(&refresh);
1464		flags.addSelectionListener(new SListener);
1465		flags.addDisposeListener(new DListener);
1466
1467		new TableTextEdit(_comm, prop, flags, 0, &nameEditEnd, null);
1468		new TableComboEdit!Combo(_comm, prop, flags, 1, &initCombo, &initEditEnd, null);
1469
1470		_comp.addDisposeListener(new Dispose);
1471
1472		return _comp;
1473	}
1474	void updateIncSearchParent(Composite incSearchParent) {
1475		auto matchers = [
1476			AdditionMatcher(MenuProps.buildMenu(.objName!Step(prop), "S", "", false), (o) => cast(Step)o !is null),
1477			AdditionMatcher(MenuProps.buildMenu(.objName!Flag(prop), "F", "", false), (o) => cast(Flag)o !is null),
1478		];
1479		_incSearch = new IncSearch(_comm, incSearchParent, matchers);
1480		_incSearch.modEvent ~= &refresh;
1481	}
1482	private Composite _comp = null;
1483	@property
1484	Control widget() {return _comp;}
1485
1486	@property
1487	package Flag[] dragFlags() {return _dragFlags;}
1488	@property
1489	package Step[] dragSteps() {return _dragSteps;}
1490
1491	private void refreshStatusLine() { mixin(S_TRACE);
1492		string s = "";
1493		void put(lazy string name, size_t count) { mixin(S_TRACE);
1494			if (!count) return;
1495			if (s.length) s ~= " ";
1496			s ~= .tryFormat(prop.msgs.flagStatus, name, count);
1497		}
1498		if (_dir) { mixin(S_TRACE);
1499			put(prop.msgs.flag, _dir.flags.length);
1500			put(prop.msgs.step, _dir.steps.length);
1501		}
1502		Flag[] selFlags;
1503		Step[] selSteps;
1504		getSelectionFlagAndStep(selFlags, selSteps);
1505		if (selFlags.length || selSteps.length) { mixin(S_TRACE);
1506			s = .tryFormat(prop.msgs.flagStatusSel, s, selFlags.length + selSteps.length);
1507		}
1508		_statusLine = s;
1509		_comm.setStatusLine(flags, _statusLine);
1510	}
1511	@property
1512	string statusLine() {return _statusLine;}
1513
1514	private void __refreshUseCount() { mixin(S_TRACE);
1515		foreach (itm; flags.getItems()) { mixin(S_TRACE);
1516			if (cast(Flag) itm.getData()) { mixin(S_TRACE);
1517				itm.setText(2, to!(string)(uc.flag.get(toFlagId((cast(Flag) itm.getData()).path))));
1518			} else { mixin(S_TRACE);
1519				assert (cast(Step) itm.getData());
1520				itm.setText(2, to!(string)(uc.step.get(toStepId((cast(Step) itm.getData()).path))));
1521			}
1522		}
1523	}
1524	void refresh() { mixin(S_TRACE);
1525		refresh(null);
1526	}
1527	void refresh(in Object selObj) { mixin(S_TRACE);
1528		if (!flags || flags.isDisposed()) return;
1529		if (_dir) { mixin(S_TRACE);
1530			const(Object)[] sels;
1531			if (selObj) { mixin(S_TRACE);
1532				sels ~= selObj;
1533			} else { mixin(S_TRACE);
1534				foreach (itm; flags.getSelection()) { mixin(S_TRACE);
1535					sels ~= itm.getData();
1536				}
1537			}
1538			flags.deselectAll();
1539			_dir.sortSteps();
1540			_dir.sortFlags();
1541			refreshFlags();
1542			foreach (i, itm; flags.getItems()) { mixin(S_TRACE);
1543				if (.contains(sels, itm.getData())) { mixin(S_TRACE);
1544					flags.select(i);
1545				}
1546			}
1547			if (selObj) { mixin(S_TRACE);
1548				flags.showSelection();
1549			}
1550		}
1551		refreshStatusLine();
1552	}
1553	/// ???????????index?
1554	@property
1555	int[] selected() { mixin(S_TRACE);
1556		if (flags && !flags.isDisposed()) { mixin(S_TRACE);
1557			return flags.getSelectionIndices();
1558		}
1559		return [];
1560	}
1561	/// ??????????????????????
1562	int[] selectedItems(out Flag[] fs, out Step[] ss) { mixin(S_TRACE);
1563		if (flags && !flags.isDisposed()) { mixin(S_TRACE);
1564			auto indices = flags.getSelectionIndices();
1565			foreach (i; indices) { mixin(S_TRACE);
1566				auto o = flags.getItem(i).getData();
1567				auto f = cast(Flag) o;
1568				if (f) fs ~= f;
1569				auto s = cast(Step) o;
1570				if (s) ss ~= s;
1571			}
1572			return indices;
1573		}
1574		return [];
1575	}
1576	/// ditto
1577	@property
1578	Flag[] selectionFlags() { mixin(S_TRACE);
1579		Flag[] fs;
1580		Step[] ss;
1581		getSelectionFlagAndStep(fs, ss);
1582		return fs;
1583	}
1584	/// ditto
1585	@property
1586	Step[] selectionSteps() { mixin(S_TRACE);
1587		Flag[] fs;
1588		Step[] ss;
1589		getSelectionFlagAndStep(fs, ss);
1590		return ss;
1591	}
1592	/// ditto
1593	@property
1594	string[] selectionFlagNames() { mixin(S_TRACE);
1595		string[] r;
1596		foreach (f; selectionFlags) { mixin(S_TRACE);
1597			r ~= f.name;
1598		}
1599		return r;
1600	}
1601	/// ditto
1602	@property
1603	string[] selectionStepNames() { mixin(S_TRACE);
1604		string[] r;
1605		foreach (f; selectionSteps) { mixin(S_TRACE);
1606			r ~= f.name;
1607		}
1608		return r;
1609	}
1610
1611	/// ???????????????????
1612	/// ??????????????????????
1613	void createFlag() { mixin(S_TRACE);
1614		editFlag(_dir, null);
1615	}
1616
1617	/// ????????????????????
1618	/// ???????????????????????
1619	void createStep() { mixin(S_TRACE);
1620		editStep(_dir, null);
1621	}
1622
1623	/// ?????????????????????
1624	void edit() { mixin(S_TRACE);
1625		if (_dir !is null) { mixin(S_TRACE);
1626			foreach (index; flags.getSelectionIndices()) { mixin(S_TRACE);
1627				if (auto step = cast(Step)flags.getItem(index).getData()) { mixin(S_TRACE);
1628					editStep(step.parent, step);
1629				} else if (auto flag = cast(Flag)flags.getItem(index).getData()) { mixin(S_TRACE);
1630					editFlag(flag.parent, flag);
1631				} else assert (0);
1632			}
1633		}
1634	}
1635	@property
1636	bool canEdit() { mixin(S_TRACE);
1637		return flags.getSelectionIndex() != -1;
1638	}
1639	/// ?????????????????
1640	void edit(Flag flag) { mixin(S_TRACE);
1641		enforce(flag.parent is dir);
1642		editFlag(flag.parent, flag);
1643	}
1644	/// ??????????????????
1645	void edit(Step step) { mixin(S_TRACE);
1646		enforce(step.parent is dir);
1647		editStep(step.parent, step);
1648	}
1649
1650	/// ?????????????????
1651	/// Params:
1652	/// dir = ???????
1653	void setDir(FlagDir dir, bool forceRefresh = false) { mixin(S_TRACE);
1654		if (!forceRefresh && _dir is dir) return;
1655		foreach (dlg; _editDlgsF.values) { mixin(S_TRACE);
1656			dlg.forceCancel();
1657		}
1658		foreach (dlg; _editDlgsS.values) { mixin(S_TRACE);
1659			dlg.forceCancel();
1660		}
1661		_dir = dir;
1662		refresh();
1663	}
1664
1665	/// Returns: ????????????
1666	@property
1667	FlagDir dir() { mixin(S_TRACE);
1668		return _dir;
1669	}
1670
1671	private void selectImpl(Object flag, bool deselect) { mixin(S_TRACE);
1672		if (deselect) flags.deselectAll();
1673		foreach (i, itm; flags.getItems()) { mixin(S_TRACE);
1674			if (itm.getData() is flag) { mixin(S_TRACE);
1675				flags.select(i);
1676				break;
1677			}
1678		}
1679		flags.showSelection();
1680		_comm.refreshToolBar();
1681	}
1682	/// ?????????
1683	void select(Flag flag, bool deselect) { mixin(S_TRACE);
1684		selectImpl(flag, deselect);
1685	}
1686	/// ??????????
1687	void select(Step step, bool deselect) { mixin(S_TRACE);
1688		selectImpl(step, deselect);
1689	}
1690	void deselectAll() { mixin(S_TRACE);
1691		flags.deselectAll();
1692	}
1693
1694	/// ????????????
1695	void dispose() { mixin(S_TRACE);
1696		if (flags !is null) { mixin(S_TRACE);
1697			flags.dispose();
1698		}
1699	}
1700
1701	/// ??????????????
1702	/// Params:
1703	/// uc = ?????????
1704	@property
1705	void useCounter(UseCounter uc) { mixin(S_TRACE);
1706		this.uc = uc;
1707	}
1708
1709	override {
1710		void cut(SelectionEvent se) { mixin(S_TRACE);
1711			if (!_dir) return;
1712			copy(se);
1713			del(se);
1714		}
1715		void copy(SelectionEvent se) { mixin(S_TRACE);
1716			if (!_dir) return;
1717			Flag[] fs;
1718			Step[] ss;
1719			if (getSelectionFlagAndStep(fs, ss)) { mixin(S_TRACE);
1720				XMLtoCB(prop, _comm.clipboard, getXML(_dir, fs, ss));
1721				_comm.refreshToolBar();
1722			}
1723		}
1724		void paste(SelectionEvent se) { mixin(S_TRACE);
1725			if (!_dir) return;
1726			auto c = CBtoXML(_comm.clipboard);
1727			if (c) { mixin(S_TRACE);
1728				try { mixin(S_TRACE);
1729					string newPath;
1730					string rootId;
1731					Flag[string] cFlags;
1732					Step[string] cSteps;
1733					auto selsF = selectionFlagNames;
1734					auto selsS = selectionStepNames;
1735					auto ver = new XMLInfo(prop.sys, LATEST_VERSION);
1736					if (_dir.appendFromXML(c, ver, true, false, cFlags, cSteps, newPath, rootId)) { mixin(S_TRACE);
1737						string[] flagName;
1738						string[] stepName;
1739						foreach (f; cFlags) { mixin(S_TRACE);
1740							flagName ~= f.name;
1741						}
1742						foreach (s; cSteps) { mixin(S_TRACE);
1743							stepName ~= s.name;
1744						}
1745						storeInsert(selsF, selsS, flagName, stepName);
1746						refresh();
1747						_comm.refFlagAndStep.call(cFlags.values, cSteps.values);
1748						_comm.refreshToolBar();
1749					}
1750				} catch (Exception e) {
1751					debugln(e);
1752				}
1753			}
1754		}
1755		void del(SelectionEvent se) { mixin(S_TRACE);
1756			if (!_dir) return;
1757			auto selsF = selectionFlagNames;
1758			auto selsS = selectionStepNames;
1759			Flag[] fs;
1760			Step[] ss;
1761			foreach (itm; flags.getSelection()) { mixin(S_TRACE);
1762				auto data = itm.getData();
1763				auto flag = cast(Flag) data;
1764				if (flag) { mixin(S_TRACE);
1765					fs ~= flag;
1766					_dir.remove(flag);
1767				}
1768				auto step = cast(Step) data;
1769				if (step) { mixin(S_TRACE);
1770					ss ~= step;
1771					_dir.remove(step);
1772				}
1773			}
1774			storeDelete(selsF, selsS, fs, ss);
1775			_comm.delFlagAndStep.call(fs, ss);
1776			refresh();
1777			_comm.refreshToolBar();
1778		}
1779		void clone(SelectionEvent se) { mixin(S_TRACE);
1780			_comm.clipboard.memoryMode = true;
1781			scope (exit) _comm.clipboard.memoryMode = false;
1782			copy(se);
1783			paste(se);
1784		}
1785		@property
1786		bool canDoTCPD() { mixin(S_TRACE);
1787			return _comm.summary && flags.isFocusControl();
1788		}
1789		@property
1790		bool canDoT() { mixin(S_TRACE);
1791			return flags.getSelectionIndex() != -1;
1792		}
1793		@property
1794		bool canDoC() { mixin(S_TRACE);
1795			return canDoT;
1796		}
1797		@property
1798		bool canDoP() { mixin(S_TRACE);
1799			return _comm.summary !is null && CBisXML(_comm.clipboard);
1800		}
1801		@property
1802		bool canDoD() { mixin(S_TRACE);
1803			return canDoT;
1804		}
1805		@property
1806		bool canDoClone() { mixin(S_TRACE);
1807			return canDoC;
1808		}
1809	}
1810	void undo() { mixin(S_TRACE);
1811		_undo.undo();
1812		_comm.refreshToolBar();
1813	}
1814	void redo() { mixin(S_TRACE);
1815		_undo.redo();
1816		_comm.refreshToolBar();
1817	}
1818
1819	void replaceID() {
1820		auto index = flags.getSelectionIndex();
1821		if (index <= -1) return;
1822		auto data = flags.getItem(index).getData();
1823		if (auto f = cast(Flag)data) _comm.replaceID(toFlagId(f.path), true);
1824		if (auto f = cast(Step)data) _comm.replaceID(toStepId(f.path), true);
1825	}
1826	@property
1827	bool canReplaceID() {
1828		auto index = flags.getSelectionIndex();
1829		if (index <= -1) return false;
1830		auto data = flags.getItem(index).getData();
1831		return cast(Flag)data || cast(Step)data;
1832	}
1833
1834	@property
1835	string[] openedCWXPath() { mixin(S_TRACE);
1836		string[] r;
1837		Flag[] fs;
1838		Step[] ss;
1839		getSelectionFlagAndStep(fs, ss);
1840		foreach (f; fs) { mixin(S_TRACE);
1841			r ~= f.cwxPath(true);
1842		}
1843		foreach (s; ss) { mixin(S_TRACE);
1844			r ~= s.cwxPath(true);
1845		}
1846		return r;
1847	}
1848}