PageRenderTime 7ms CodeModel.GetById 8ms app.highlight 150ms RepoModel.GetById 1ms app.codeStats 1ms

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

https://bitbucket.org/k4nagatsuki/cwxeditor
D | 3121 lines | 3052 code | 58 blank | 11 comment | 478 complexity | 63095cde6e618b1a5cbda9fc2155e05c MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1
   2module cwx.editor.gui.dwt.areatable;
   3
   4import cwx.flag;
   5import cwx.area;
   6import cwx.event;
   7import cwx.utils;
   8import cwx.summary;
   9import cwx.background;
  10import cwx.usecounter;
  11import cwx.xml;
  12import cwx.skin;
  13import cwx.path;
  14import cwx.structs;
  15import cwx.menu;
  16import cwx.types;
  17import cwx.card;
  18import cwx.system;
  19
  20import cwx.editor.gui.dwt.smalldialogs;
  21import cwx.editor.gui.dwt.dprops;
  22import cwx.editor.gui.dwt.dutils;
  23import cwx.editor.gui.dwt.dskin;
  24import cwx.editor.gui.dwt.flagtable;
  25import cwx.editor.gui.dwt.areaview;
  26import cwx.editor.gui.dwt.areawindow;
  27import cwx.editor.gui.dwt.eventwindow;
  28import cwx.editor.gui.dwt.summarydialog;
  29import cwx.editor.gui.dwt.commons;
  30import cwx.editor.gui.dwt.properties;
  31import cwx.editor.gui.dwt.xmlbytestransfer;
  32import cwx.editor.gui.dwt.undo;
  33import cwx.editor.gui.dwt.dmenu;
  34import cwx.editor.gui.dwt.customtable;
  35import cwx.editor.gui.dwt.incsearch;
  36import cwx.editor.gui.dwt.splitpane;
  37import cwx.editor.gui.dwt.centerlayout;
  38
  39import std.algorithm : max, min;
  40import std.array : split;
  41import std.conv;
  42import std.string : icmp, join, toLower;
  43
  44import org.eclipse.swt.all;
  45
  46import java.lang.all;
  47
  48/// ??????????????????????
  49class AreaTable : TCPD {
  50private:
  51	int compType(const Object o1, const Object o2) { mixin(S_TRACE);
  52		if (cast(const Summary) o1) return -1;
  53		if (cast(const Summary) o2) return 1;
  54		if (cast(const Area) o1) { mixin(S_TRACE);
  55			if (cast(const Battle) o2) return -1;
  56			if (cast(const Package) o2) return -1;
  57		}
  58		if (cast(const Battle) o1) { mixin(S_TRACE);
  59			if (cast(const Area) o2) return 1;
  60			if (cast(const Package) o2) return -1;
  61		}
  62		if (cast(const Package) o1) { mixin(S_TRACE);
  63			if (cast(const Area) o2) return 1;
  64			if (cast(const Battle) o2) return 1;
  65		}
  66		return 0;
  67	}
  68	bool compID(const Object o1, const Object o2) { mixin(S_TRACE);
  69		auto c = compType(o1, o2);
  70		if (c == 0) { mixin(S_TRACE);
  71			auto a1 = cast(const AbstractArea) o1;
  72			auto a2 = cast(const AbstractArea) o2;
  73			if (a1.id < a2.id) return true;
  74			if (a1.id > a2.id) return false;
  75			return false;
  76		}
  77		return c < 0;
  78	}
  79	bool compName(const Object o1, const Object o2) { mixin(S_TRACE);
  80		auto c = compType(o1, o2);
  81		if (c == 0) { mixin(S_TRACE);
  82			auto a1 = cast(const AbstractArea) o1;
  83			auto a2 = cast(const AbstractArea) o2;
  84
  85			if (_prop.var.etc.logicalSort) { mixin(S_TRACE);
  86				c = incmp(a1.name, a2.name);
  87			} else { mixin(S_TRACE);
  88				c = icmp(a1.name, a2.name);
  89			}
  90			if (c == 0) return compID(o1, o2);
  91		}
  92		return c < 0;
  93	}
  94	bool compUC(const Object o1, const Object o2) { mixin(S_TRACE);
  95		auto c = compType(o1, o2);
  96		if (c == 0) { mixin(S_TRACE);
  97			int uc1 = 0;
  98			int uc2 = 0;
  99			auto a1 = cast(const Area) o1;
 100			auto a2 = cast(const Area) o2;
 101			if (a1 && a2) { mixin(S_TRACE);
 102				uc1 = _summ.useCounter.get(toAreaId(a1.id));
 103				uc2 = _summ.useCounter.get(toAreaId(a2.id));
 104			}
 105			auto b1 = cast(const Battle) o1;
 106			auto b2 = cast(const Battle) o2;
 107			if (b1 && b2) { mixin(S_TRACE);
 108				uc1 = _summ.useCounter.get(toBattleId(b1.id));
 109				uc2 = _summ.useCounter.get(toBattleId(b2.id));
 110			}
 111			auto p1 = cast(const Package) o1;
 112			auto p2 = cast(const Package) o2;
 113			if (p1 && p2) { mixin(S_TRACE);
 114				uc1 = _summ.useCounter.get(toPackageId(p1.id));
 115				uc2 = _summ.useCounter.get(toPackageId(p2.id));
 116			}
 117
 118			if (uc1 < uc2) return true;
 119			if (uc1 > uc2) return false;
 120			return compID(o1, o2);
 121		}
 122		return c < 0;
 123	}
 124	bool revCompID(const Object o1, const Object o2) { mixin(S_TRACE);
 125		auto c = compType(o2, o1);
 126		if (c == 0) { mixin(S_TRACE);
 127			auto a1 = cast(const AbstractArea) o2;
 128			auto a2 = cast(const AbstractArea) o1;
 129			if (a1.id < a2.id) return true;
 130			if (a1.id > a2.id) return false;
 131			return false;
 132		}
 133		return 0 < c;
 134	}
 135	bool revCompName(const Object o1, const Object o2) { mixin(S_TRACE);
 136		auto c = compType(o2, o1);
 137		if (c == 0) { mixin(S_TRACE);
 138			auto a1 = cast(const AbstractArea) o2;
 139			auto a2 = cast(const AbstractArea) o1;
 140
 141			if (_prop.var.etc.logicalSort) { mixin(S_TRACE);
 142				c = incmp(a2.name, a1.name);
 143			} else { mixin(S_TRACE);
 144				c = icmp(a2.name, a1.name);
 145			}
 146			if (c == 0) return revCompID(o1, o2);
 147		}
 148		return 0 < c;
 149	}
 150	bool revCompUC(const Object o1, const Object o2) { mixin(S_TRACE);
 151		auto c = compType(o2, o1);
 152		if (c == 0) { mixin(S_TRACE);
 153			int uc1 = 0;
 154			int uc2 = 0;
 155			auto a1 = cast(const Area) o2;
 156			auto a2 = cast(const Area) o1;
 157			if (a1 && a2) { mixin(S_TRACE);
 158				uc1 = _summ.useCounter.get(toAreaId(a1.id));
 159				uc2 = _summ.useCounter.get(toAreaId(a2.id));
 160			}
 161			auto b1 = cast(const Battle) o2;
 162			auto b2 = cast(const Battle) o1;
 163			if (b1 && b2) { mixin(S_TRACE);
 164				uc1 = _summ.useCounter.get(toBattleId(b1.id));
 165				uc2 = _summ.useCounter.get(toBattleId(b2.id));
 166			}
 167			auto p1 = cast(const Package) o2;
 168			auto p2 = cast(const Package) o1;
 169			if (p1 && p2) { mixin(S_TRACE);
 170				uc1 = _summ.useCounter.get(toPackageId(p1.id));
 171				uc2 = _summ.useCounter.get(toPackageId(p2.id));
 172			}
 173
 174			if (uc1 < uc2) return true;
 175			if (uc1 > uc2) return false;
 176			return revCompID(o1, o2);
 177		}
 178		return 0 < c;
 179	}
 180
 181	private static void saveIDs(Summary summ, out ulong[] areaIDs, out ulong[] battleIDs, out ulong[] packageIDs) { mixin(S_TRACE);
 182		areaIDs.length = 0;
 183		foreach (a; summ.areas) areaIDs ~= a.id;
 184		battleIDs.length = 0;
 185		foreach (a; summ.battles) battleIDs ~= a.id;
 186		packageIDs.length = 0;
 187		foreach (a; summ.packages) packageIDs ~= a.id;
 188	}
 189
 190	static class ATUndo : Undo {
 191		protected AreaTable _v = null;
 192		protected Commons comm;
 193		protected Summary summ;
 194		protected bool one = true;
 195		protected AbstractArea[] calls, delCalls;
 196
 197		private ulong[] _areaIDs;
 198		private ulong[] _areaIDsB;
 199		private ulong[] _battleIDs;
 200		private ulong[] _battleIDsB;
 201		private ulong[] _packageIDs;
 202		private ulong[] _packageIDsB;
 203		private ulong _sel;
 204		private TypeInfo _selType;
 205		private ulong _selB;
 206		private TypeInfo _selTypeB;
 207		private DirTree _dirs, _dirsB;
 208
 209		this (AreaTable v, Commons comm, Summary summ) { mixin(S_TRACE);
 210			_v = v;
 211			this.comm = comm;
 212			this.summ = summ;
 213
 214			saveIDs(v);
 215		}
 216		private void saveIDs(AreaTable v) { mixin(S_TRACE);
 217			AreaTable.saveIDs(summ, _areaIDs, _battleIDs, _packageIDs);
 218			if (v && v._areas && !v._areas.isDisposed()) { mixin(S_TRACE);
 219				v.getSelectionInfo(_sel, _selType);
 220			}
 221			if (v) {
 222				_dirs = v._dirs.dup;
 223			}
 224		}
 225		abstract override void undo();
 226		abstract override void redo();
 227		abstract override void dispose();
 228		protected void udb(AreaTable v) { mixin(S_TRACE);
 229			_areaIDsB = _areaIDs.dup;
 230			_battleIDsB = _battleIDs.dup;
 231			_packageIDsB = _packageIDs.dup;
 232			_selB = _sel;
 233			_selTypeB = _selType;
 234			_dirsB = _dirs;
 235			saveIDs(v);
 236			if (!one) return;
 237			if (v && v._areas && !v._areas.isDisposed()) { mixin(S_TRACE);
 238				.forceFocus(v._areas, false);
 239			}
 240			if (v._parent) v._parent.setRedraw(false);
 241		}
 242		private void resetID(alias ToID, A)(AreaTable v, A[] arr, ulong[] ids) { mixin(S_TRACE);
 243			ulong[] oldIDs;
 244			foreach (i, a; arr) { mixin(S_TRACE);
 245				auto oID = a.id;
 246				a.id = ulong.max - arr.length + i;
 247				summ.useCounter.change(ToID(oID), ToID(a.id));
 248				oldIDs ~= oID;
 249			}
 250			foreach (i, a; arr) { mixin(S_TRACE);
 251				auto oID = a.id;
 252				a.id = ids[i];
 253				summ.useCounter.change(ToID(oID), ToID(a.id));
 254			}
 255			foreach (i, a; arr) { mixin(S_TRACE);
 256				if (a.id != oldIDs[i] || calls.contains(a)) { mixin(S_TRACE);
 257					staticCallRefArea(v, comm, a);
 258				}
 259			}
 260		}
 261		protected ulong uid(ulong id, TypeInfo type) { mixin(S_TRACE);
 262			return id;
 263		}
 264		protected void uda(AreaTable v) { mixin(S_TRACE);
 265			resetID!toAreaId(v, summ.areas, _areaIDsB);
 266			resetID!toBattleId(v, summ.battles, _battleIDsB);
 267			resetID!toPackageId(v, summ.packages, _packageIDsB);
 268			foreach (area; delCalls) {
 269				auto a = cast(Area) area;
 270				if (a) { mixin(S_TRACE);
 271					comm.delArea.call(v, a);
 272				}
 273				auto b = cast(Battle) area;
 274				if (b) { mixin(S_TRACE);
 275					comm.delBattle.call(v, b);
 276				}
 277				auto p = cast(Package) area;
 278				if (p) { mixin(S_TRACE);
 279					comm.delPackage.call(v, p);
 280				}
 281			}
 282			calls = [];
 283			delCalls = [];
 284			if (!one) return;
 285			if (v) { mixin(S_TRACE);
 286				v._dirs = _dirsB.dup;
 287				v.refreshDirTree();
 288			}
 289			if (v && v._areas && !v._areas.isDisposed()) { mixin(S_TRACE);
 290				v.refreshAreas();
 291				v.selectFromInfo(_selB, _selTypeB);
 292				v.refreshStatusLine();
 293			}
 294			comm.refUseCount.call();
 295			comm.refreshToolBar();
 296			if (v._parent) v._parent.setRedraw(true);
 297		}
 298		protected AreaTable view() { mixin(S_TRACE);
 299			return _v;
 300		}
 301	}
 302	static class ATUndoArr : Undo {
 303		private ATUndo[] _array;
 304		this (ATUndo[] array) { mixin(S_TRACE);
 305			_array = array;
 306		}
 307		void undo() { mixin(S_TRACE);
 308			foreach_reverse (i, u; _array) { mixin(S_TRACE);
 309				u.one = (i == 0);
 310				u.undo();
 311			}
 312		}
 313		void redo() { mixin(S_TRACE);
 314			foreach (i, u; _array) { mixin(S_TRACE);
 315				u.one = (i + 1 == _array.length);
 316				u.redo();
 317			}
 318		}
 319		void dispose() { mixin(S_TRACE);
 320			foreach (u; _array) u.dispose();
 321		}
 322	}
 323
 324	void storeEmpty() { mixin(S_TRACE);
 325		_undo ~= new UndoIDs(this, _comm, _summ);
 326	}
 327
 328	static class UndoIDs : ATUndo {
 329		this (AreaTable v, Commons comm, Summary summ) { mixin(S_TRACE);
 330			super (v, comm, summ);
 331		}
 332		override void undo() { mixin(S_TRACE);
 333			auto v = view();
 334			udb(v);
 335			scope (exit) uda(v);
 336		}
 337		override void redo() { mixin(S_TRACE);
 338			auto v = view();
 339			udb(v);
 340			scope (exit) uda(v);
 341		}
 342		override void dispose() {}
 343	}
 344	static class UndoEdit : ATUndo {
 345		private string _name;
 346		private ulong _id;
 347		private TypeInfo _type;
 348
 349		static struct SummData {
 350			string scenarioName;
 351			string imagePath;
 352			string author;
 353			int levelMin, levelMax;
 354			string desc;
 355			string type;
 356			string[] rCoupons;
 357			uint rCouponNum;
 358			ulong startArea;
 359			Skin skin;
 360			this (Commons comm, Summary summ) { mixin(S_TRACE);
 361				scenarioName = summ.scenarioName;
 362				imagePath = summ.imagePath;
 363				author = summ.author;
 364				levelMin = summ.levelMin;
 365				levelMax = summ.levelMax;
 366				desc = summ.desc;
 367				type = summ.type;
 368				rCoupons = summ.rCoupons.dup;
 369				rCouponNum = summ.rCouponNum;
 370				startArea = summ.startArea;
 371				skin = comm.skin;
 372			}
 373			void toSummary(Commons comm, Summary summ) { mixin(S_TRACE);
 374				summ.scenarioName = scenarioName;
 375				summ.imagePath = imagePath;
 376				summ.author = author;
 377				summ.levelMin = levelMin;
 378				summ.levelMax = levelMax;
 379				summ.desc = desc;
 380				summ.type = type;
 381				summ.rCoupons = rCoupons;
 382				summ.rCouponNum = rCouponNum;
 383				summ.startArea = startArea;
 384				comm.skin = skin;
 385			}
 386		}
 387		private SummData _summData;
 388
 389		this (AreaTable v, Commons comm, Summary summ, ulong id, TypeInfo type) { mixin(S_TRACE);
 390			super (v, comm, summ);
 391			if (0 == id) { mixin(S_TRACE);
 392				_summData = SummData(comm, summ);
 393			} else { mixin(S_TRACE);
 394				_name = areaFromInfo(summ, id, type).name;
 395			}
 396			_id = id;
 397			_type = type;
 398		}
 399		private void impl() { mixin(S_TRACE);
 400			auto v = view();
 401			udb(v);
 402			scope (exit) uda(v);
 403			if (0 == _id) { mixin(S_TRACE);
 404				auto oldData = SummData(comm, summ);
 405				bool refSkin = oldData.skin !is _summData.skin;
 406				_summData.toSummary(comm, summ);
 407				_summData = oldData;
 408				if (v && v._areas && !v._areas.isDisposed()) { mixin(S_TRACE);
 409					auto itm = v.getItemFrom(uid(_id, _type), _type);
 410					if (itm) itm.setText(NAME, summ.scenarioName);
 411				}
 412				comm.refScenarioName.call(v);
 413				if (refSkin) comm.refSkin.call();
 414			} else { mixin(S_TRACE);
 415				auto area = areaFromInfo(summ, uid(_id, _type), _type);
 416				string oldName = area.name;
 417				area.name = _name;
 418				_name = oldName;
 419				if (v && v._areas && !v._areas.isDisposed()) { mixin(S_TRACE);
 420					auto itm = v.getItemFrom(uid(_id, _type), _type);
 421					if (itm) itm.setText(NAME, area.name);
 422				}
 423				calls ~= area;
 424			}
 425			comm.refUseCount.call();
 426		}
 427		override void undo() { mixin(S_TRACE);
 428			impl();
 429		}
 430		override void redo() { mixin(S_TRACE);
 431			impl();
 432		}
 433		override void dispose() {}
 434	}
 435	void storeEdit(int index) { mixin(S_TRACE);
 436		ulong id;
 437		TypeInfo type;
 438		getInfo(index, id, type);
 439		storeEdit(id, type);
 440	}
 441	void storeEdit(ulong id, TypeInfo type) { mixin(S_TRACE);
 442		_undo ~= new UndoEdit(this, _comm, _summ, id, type);
 443	}
 444	static class UndoMove : ATUndo {
 445		private int _removeIndex, _insertIndex;
 446		private TypeInfo _type;
 447		this (AreaTable v, Commons comm, Summary summ, int removeIndex, int insertIndex, TypeInfo type) { mixin(S_TRACE);
 448			super (v, comm, summ);
 449			_removeIndex = removeIndex;
 450			_insertIndex = insertIndex;
 451			if (_removeIndex < _insertIndex) _insertIndex--;
 452			_type = type;
 453		}
 454		private void impl() { mixin(S_TRACE);
 455			auto v = view();
 456			udb(v);
 457			scope (exit) uda(v);
 458
 459			int removeIndex = _removeIndex;
 460			int insertIndex = _insertIndex;
 461			if (insertIndex < removeIndex) removeIndex++;
 462			if (_type is typeid(Area)) { mixin(S_TRACE);
 463				auto a = summ.areas[insertIndex];
 464				summ.insert(removeIndex, a);
 465			} else if (_type is typeid(Battle)) { mixin(S_TRACE);
 466				auto a = summ.battles[insertIndex];
 467				summ.insert(removeIndex, a);
 468			} else if (_type is typeid(Package)) { mixin(S_TRACE);
 469				auto a = summ.packages[insertIndex];
 470				summ.insert(removeIndex, a);
 471			}
 472
 473			std.algorithm.swap(_removeIndex, _insertIndex);
 474		}
 475		override void undo() { mixin(S_TRACE);
 476			impl();
 477		}
 478		override void redo() { mixin(S_TRACE);
 479			impl();
 480		}
 481		override void dispose() {}
 482	}
 483	void storeMove(int removeIndex, int insertIndex, TypeInfo type) { mixin(S_TRACE);
 484		assert (_areas.getSortColumn() is null || _areas.getSortColumn() is _idSorter.column);
 485		_undo ~= new UndoMove(this, _comm, _summ, removeIndex, insertIndex, type);
 486	}
 487	static class UndoSwap : ATUndo {
 488		private int _index1, _index2;
 489		this (AreaTable v, Commons comm, Summary summ, int index1, int index2) { mixin(S_TRACE);
 490			super (v, comm, summ);
 491			_index1 = index1;
 492			_index2 = index2;
 493		}
 494		private void impl() { mixin(S_TRACE);
 495			auto v = view();
 496			udb(v);
 497			scope (exit) uda(v);
 498			auto area1 = areaFromIndex(summ, _index1);
 499			auto area2 = areaFromIndex(summ, _index2);
 500			auto a = cast(Area) area1;
 501			if (a) { mixin(S_TRACE);
 502				summ.swap!Area(toAreaIndex(summ, _index1), toAreaIndex(summ, _index2));
 503			}
 504			auto b = cast(Battle) area1;
 505			if (b) { mixin(S_TRACE);
 506				summ.swap!Battle(toBattleIndex(summ, _index1), toBattleIndex(summ, _index2));
 507			}
 508			auto p = cast(Package) area1;
 509			if (p) { mixin(S_TRACE);
 510				summ.swap!Package(toPackageIndex(summ, _index1), toPackageIndex(summ, _index2));
 511			}
 512			calls ~= area1;
 513			calls ~= area2;
 514			std.algorithm.swap(_index1, _index2);
 515		}
 516		override void undo() { mixin(S_TRACE);
 517			impl();
 518		}
 519		override void redo() { mixin(S_TRACE);
 520			impl();
 521		}
 522		override void dispose() {}
 523	}
 524	void storeSwap(int index1, int index2) { mixin(S_TRACE);
 525		assert (_areas.getSortColumn() is null || _areas.getSortColumn() is _idSorter.column);
 526		_undo ~= new UndoSwap(this, _comm, _summ, index1, index2);
 527	}
 528	static class UndoInsertDelete : ATUndo {
 529		private bool _insert;
 530
 531		private ulong _id;
 532		private TypeInfo _type;
 533
 534		private AbstractArea _area = null;
 535		private bool _isStartArea = false;
 536		private int _delIndex = -1;
 537
 538		this (AreaTable v, Commons comm, Summary summ, ulong id, TypeInfo type, bool insert, ulong[] a, ulong[] b, ulong[] p) { mixin(S_TRACE);
 539			super (v, comm, summ);
 540			_insert = insert;
 541			_id = id;
 542			_type = type;
 543			if (insert) { mixin(S_TRACE);
 544				_areaIDs = a;
 545				_battleIDs = b;
 546				_packageIDs = p;
 547			} else { mixin(S_TRACE);
 548				initUndoDelete();
 549			}
 550		}
 551		private void initUndoDelete() { mixin(S_TRACE);
 552			auto area = areaFromInfo(summ, uid(_id, _type), _type);
 553			_delIndex = toIndexFrom(summ, uid(_id, _type), _type);
 554			_isStartArea = cast(Area) area && summ.startArea == area.id;
 555			_area = area.dup;
 556			_area.setUseCounter(summ.useCounter.sub);
 557		}
 558		private void undoInsert() { mixin(S_TRACE);
 559			auto v = view();
 560			udb(v);
 561			scope (exit) uda(v);
 562			_insert = false;
 563			initUndoDelete();
 564			if (v && v._areas && !v._areas.isDisposed()) { mixin(S_TRACE);
 565				auto itm = v.getItemFrom(uid(_id, _type), _type);
 566				if (itm) itm.dispose();
 567			}
 568			auto area = areaFromInfo(summ, uid(_id, _type), _type);
 569			delCalls ~= area;
 570			summ.remove(area);
 571			comm.refUseCount.call();
 572		}
 573		void undoDelete() { mixin(S_TRACE);
 574			auto v = view();
 575			udb(v);
 576			scope (exit) uda(v);
 577			scope (exit) _area = null;
 578			_insert = true;
 579			_area.removeUseCounter();
 580			auto a = cast(Area) _area;
 581			calls ~= _area;
 582			if (a) { mixin(S_TRACE);
 583				summ.insert(_delIndex, a);
 584				if (v && v._areas && !v._areas.isDisposed()) v.refreshAreas();
 585				if (_isStartArea) { mixin(S_TRACE);
 586					summ.startArea = a.id;
 587					_isStartArea = false;
 588				}
 589				return;
 590			}
 591			auto b = cast(Battle) _area;
 592			if (b) { mixin(S_TRACE);
 593				summ.insert(_delIndex, b);
 594				if (v && v._areas && !v._areas.isDisposed()) v.refreshAreas();
 595				return;
 596			}
 597			auto p = cast(Package) _area;
 598			if (p) { mixin(S_TRACE);
 599				summ.insert(_delIndex, p);
 600				if (v && v._areas && !v._areas.isDisposed()) v.refreshAreas();
 601				return;
 602			}
 603			assert (0);
 604		}
 605		override void undo() { mixin(S_TRACE);
 606			if (_insert) { mixin(S_TRACE);
 607				undoInsert();
 608			} else { mixin(S_TRACE);
 609				undoDelete();
 610			}
 611		}
 612		override void redo() { mixin(S_TRACE);
 613			undo();
 614		}
 615		override void dispose() { mixin(S_TRACE);
 616			if (_area) { mixin(S_TRACE);
 617				_area.removeUseCounter();
 618			}
 619		}
 620	}
 621	void storeInsert(ulong id, TypeInfo type, ulong[] a, ulong[] b, ulong[] p) { mixin(S_TRACE);
 622		_undo ~= new UndoInsertDelete(this, _comm, _summ, id, type, true, a, b, p);
 623	}
 624	void storeDelete(int index) { mixin(S_TRACE);
 625		ulong id;
 626		TypeInfo type;
 627		getInfo(index, id, type);
 628		_undo ~= new UndoInsertDelete(this, _comm, _summ, id, type, false, [], [], []);
 629	}
 630	static class UndoRenameDir : ATUndo {
 631		private string _oldPath, _newPath;
 632		this (AreaTable v, Commons comm, Summary summ, string oldPath, string newPath) { mixin(S_TRACE);
 633			super (v, comm, summ);
 634			_oldPath = oldPath;
 635			_newPath = newPath;
 636		}
 637		private void impl() { mixin(S_TRACE);
 638			auto v = view();
 639			udb(v);
 640			scope (exit) uda(v);
 641			void put(AbstractArea area) { mixin(S_TRACE);
 642				if (area.dirName == _newPath) { mixin(S_TRACE);
 643					area.dirName = _oldPath;
 644					calls ~= area;
 645				}
 646			}
 647			foreach (a; summ.areas) put(a);
 648			foreach (a; summ.battles) put(a);
 649			foreach (a; summ.packages) put(a);
 650			std.algorithm.swap(_oldPath, _newPath);
 651
 652			if (v && v._areas && !v._areas.isDisposed()) {
 653				auto itm = v.findDirTree(_oldPath);
 654				auto dir = cast(DirTree)itm.getData();
 655				dir.name = .split(_newPath, "\\")[$ - 1];
 656				v._dir = dir.path;
 657				v.refreshAreas();
 658			}
 659		}
 660		override void undo() { mixin(S_TRACE);
 661			impl();
 662		}
 663		override void redo() { mixin(S_TRACE);
 664			impl();
 665		}
 666		override void dispose() {}
 667	}
 668	void storeRenameDir(string oldPath, string newPath) {
 669		_undo ~= new UndoRenameDir(this, _comm, _summ, oldPath, newPath);
 670	}
 671
 672	void dirEditEnd(TreeItem itm, Control ctrl) { mixin(S_TRACE);
 673		if (_readOnly) return;
 674		assert (_dirMode);
 675		auto t = cast(Text)ctrl;
 676		if (!t) return;
 677		auto newText = std.array.replace(t.getText(), "\\", "");
 678		if (newText == "") return;
 679		if (itm.getText() == newText) return;
 680		if (auto summ = cast(Summary)itm.getData()) { mixin(S_TRACE);
 681			storeEdit(0UL, null);
 682			summ.scenarioName = newText;
 683			itm.setText(newText);
 684			_comm.refScenarioName.call();
 685		} else { mixin(S_TRACE);
 686			auto dir = cast(DirTree)itm.getData();
 687			auto oldPath = dir.path;
 688			dir.name = createNewName(newText, (s) {
 689				foreach (n; dir.parent.subDirs) {
 690					if (n is dir) continue;
 691					if (0 == icmp(n.name, s)) {
 692						return false;
 693					}
 694				}
 695				return true;
 696			});
 697			auto path = dir.path;
 698			itm.setText(dir.name);
 699			storeRenameDir(oldPath, path);
 700			void put(AbstractArea area) {
 701				if (area.dirName == oldPath) {
 702					area.dirName = path;
 703					callRefArea(area);
 704				}
 705			}
 706			foreach (a; _summ.areas) put(a);
 707			foreach (a; _summ.battles) put(a);
 708			foreach (a; _summ.packages) put(a);
 709			sortDirTree();
 710			refreshDirTree();
 711			updateDirSel();
 712		}
 713		_comm.refreshToolBar();
 714	}
 715
 716	void editEnd(TableItem itm, int column, string newText) { mixin(S_TRACE);
 717		if (_readOnly) return;
 718		assert (column == 1);
 719		if (!newText.length) return;
 720		if (auto summ = cast(Summary) itm.getData()) { mixin(S_TRACE);
 721			if (summ.scenarioName == newText) return;
 722			storeEdit(0UL, null);
 723			summ.scenarioName = newText;
 724			itm.setText(NAME, newText);
 725			_comm.refScenarioName.call();
 726		} else if (auto area = cast(AbstractArea) itm.getData()) { mixin(S_TRACE);
 727			assert (area !is null);
 728			if (_dirMode) { mixin(S_TRACE);
 729				newText = std.array.replace(newText, "\\", "");
 730				if (newText == "") return;
 731				if (area.baseName == newText) return;
 732				storeEdit(_areas.indexOf(itm));
 733				area.baseName = newText;
 734			} else { mixin(S_TRACE);
 735				if (area.name == newText) return;
 736				storeEdit(_areas.indexOf(itm));
 737				area.name = newText;
 738			}
 739			refreshAreas();
 740			callRefArea(area);
 741		}
 742		_comm.refreshToolBar();
 743	}
 744	private static const ID = 0;
 745	private static const NAME = 1;
 746	private static const UC = 2;
 747
 748	int _readOnly = SWT.NONE;
 749
 750	bool _dirMode = false;
 751	string _dir = "";
 752
 753	Commons _comm;
 754	Props _prop;
 755	Composite _parent = null;
 756	FlagTable _flags = null;
 757	Summary _summ, _importTarget = null;
 758	TableSorter!Object _idSorter;
 759	TableSorter!Object _nameSorter;
 760	TableSorter!Object _ucSorter;
 761
 762	Tree _dirTree = null;
 763	Table _areas;
 764	TableTextEdit _areasEdit;
 765
 766	TreeEdit _areaDirEdit = null;
 767	DirTree _dirs = null;
 768
 769	IncSearch _incSearch = null;
 770	private void incSearch() { mixin(S_TRACE);
 771		.forceFocus(_areas, true);
 772		_incSearch.startIncSearch();
 773	}
 774
 775	UndoManager _undo;
 776
 777	string _statusLine = "";
 778	void refreshStatusLine() { mixin(S_TRACE);
 779		string s = "";
 780		void put(lazy string name, size_t count) { mixin(S_TRACE);
 781			if (!count) return;
 782			if (s.length) s ~= " ";
 783			s ~= .tryFormat(_prop.msgs.areaStatus, name, count);
 784		}
 785		if (_summ) { mixin(S_TRACE);
 786			put(_prop.msgs.area, areaCount);
 787			put(_prop.msgs.battle, battleCount);
 788			put(_prop.msgs.cwPackage, packageCount);
 789		}
 790		_statusLine = s;
 791		_comm.setStatusLine(_areas, _statusLine);
 792	}
 793	size_t count(A)(A[] areas) { mixin(S_TRACE);
 794		if (_dirMode) { mixin(S_TRACE);
 795			size_t n = 0;
 796			foreach (a; areas) { mixin(S_TRACE);
 797				if (0 == icmp(a.dirName, _dir)) n++;
 798			}
 799			return n;
 800		} else { mixin(S_TRACE);
 801			return areas.length;
 802		}
 803	}
 804	@property
 805	size_t areaCount() { return count(_summ.areas); }
 806	@property
 807	size_t battleCount() { return count(_summ.battles); }
 808	@property
 809	size_t packageCount() { return count(_summ.packages); }
 810
 811	void getSelectionInfo(out ulong id, out TypeInfo type) { mixin(S_TRACE);
 812		getInfo(_areas.getSelectionIndex(), id, type);
 813	}
 814	void getInfo(int index, out ulong id, out TypeInfo type) { mixin(S_TRACE);
 815		id = 0;
 816		type = null;
 817		if (0 <= index) { mixin(S_TRACE);
 818			auto d = _areas.getItem(index).getData();
 819			auto a = cast(AbstractArea)d;
 820			if (a)getInfoFromArea(a, id, type);
 821		}
 822	}
 823	void getInfoFromArea(in AbstractArea d, out ulong id, out TypeInfo type) { mixin(S_TRACE);
 824		auto a = cast(Area) d;
 825		if (a) { mixin(S_TRACE);
 826			id = a.id;
 827			type = typeid(Area);
 828		}
 829		auto b = cast(Battle) d;
 830		if (b) { mixin(S_TRACE);
 831			id = b.id;
 832			type = typeid(Battle);
 833		}
 834		auto p = cast(Package) d;
 835		if (p) { mixin(S_TRACE);
 836			id = p.id;
 837			type = typeid(Package);
 838		}
 839	}
 840	void selectFromInfo(ulong id, in TypeInfo type) { mixin(S_TRACE);
 841		if (type is typeid(Area)) { mixin(S_TRACE);
 842			select(_summ.area(id));
 843		} else if (type is typeid(Battle)) { mixin(S_TRACE);
 844			select(_summ.battle(id));
 845		} else if (type is typeid(Package)) { mixin(S_TRACE);
 846			select(_summ.cwPackage(id));
 847		} else { mixin(S_TRACE);
 848			if (_dirMode) { mixin(S_TRACE);
 849				_dirTree.setSelection([_dirTree.getItem(0)]);
 850			} else { mixin(S_TRACE);
 851				if (_areas.getItemCount()) _areas.select(0);
 852			}
 853		}
 854	}
 855	static AbstractArea areaFromInfo(Summary summ, ulong id, TypeInfo type) { mixin(S_TRACE);
 856		if (type is typeid(Area)) { mixin(S_TRACE);
 857			return summ.area(id);
 858		} else if (type is typeid(Battle)) { mixin(S_TRACE);
 859			return summ.battle(id);
 860		} else if (type is typeid(Package)) { mixin(S_TRACE);
 861			return summ.cwPackage(id);
 862		} else assert (0);
 863	}
 864	TableItem getItemFrom(ulong id, TypeInfo type) { mixin(S_TRACE);
 865		if (type is typeid(Area)) { mixin(S_TRACE);
 866			foreach (itm; _areas.getItems()) { mixin(S_TRACE);
 867				if (cast(Area)itm.getData() && (cast(AbstractArea)itm.getData()).id == id) { mixin(S_TRACE);
 868					return itm;
 869				}
 870			}
 871			return null;
 872		} else if (type is typeid(Battle)) { mixin(S_TRACE);
 873			foreach (itm; _areas.getItems()) { mixin(S_TRACE);
 874				if (cast(Battle)itm.getData() && (cast(AbstractArea)itm.getData()).id == id) { mixin(S_TRACE);
 875					return itm;
 876				}
 877			}
 878			return null;
 879		} else if (type is typeid(Package)) { mixin(S_TRACE);
 880			foreach (itm; _areas.getItems()) { mixin(S_TRACE);
 881				if (cast(Package)itm.getData() && (cast(AbstractArea)itm.getData()).id == id) { mixin(S_TRACE);
 882					return itm;
 883				}
 884			}
 885			return null;
 886		}
 887		return showSummary ? _areas.getItem(0) : null;
 888	}
 889
 890	static int toIndexFrom(Summary summ, ulong id, TypeInfo type) { mixin(S_TRACE);
 891		if (type is typeid(Area)) { mixin(S_TRACE);
 892			foreach (i, a; summ.areas) { mixin(S_TRACE);
 893				if (a.id == id) return i;
 894			}
 895		} else if (type is typeid(Battle)) { mixin(S_TRACE);
 896			foreach (i, a; summ.battles) { mixin(S_TRACE);
 897				if (a.id == id) return i;
 898			}
 899		} else if (type is typeid(Package)) { mixin(S_TRACE);
 900			foreach (i, a; summ.packages) { mixin(S_TRACE);
 901				if (a.id == id) return i;
 902			}
 903		} else assert (0);
 904		return -1;
 905	}
 906	static int indexFrom(A)(Summary summ, int index) { mixin(S_TRACE);
 907		static if (is(A : Area)) {
 908			return index;
 909		} else static if (is(A : Battle)) {
 910			return index + summ.areas.length;
 911		} else static if (is(A : Package)) {
 912			return index + summ.areas.length + summ.battles.length;
 913		} else static assert (0);
 914	}
 915	static int toIndex(A)(Summary summ, int index) { mixin(S_TRACE);
 916		static if (is(A : Area)) {
 917			return index;
 918		} else static if (is(A : Battle)) {
 919			return index - summ.areas.length;
 920		} else static if (is(A : Package)) {
 921			return index - (summ.areas.length + summ.battles.length);
 922		} else static assert (0);
 923	}
 924	alias toIndex!Area toAreaIndex;
 925	alias toIndex!Battle toBattleIndex;
 926	alias toIndex!Package toPackageIndex;
 927	static AbstractArea areaFromIndex(Summary summ, int index) { mixin(S_TRACE);
 928		if (summ.areas.length + summ.battles.length <= index) { mixin(S_TRACE);
 929			return summ.packages[toPackageIndex(summ, index)];
 930		}
 931		if (summ.areas.length <= index) { mixin(S_TRACE);
 932			return summ.battles[toBattleIndex(summ, index)];
 933		}
 934		if (0 <= index) { mixin(S_TRACE);
 935			return summ.areas[toAreaIndex(summ, index)];
 936		}
 937		return null;
 938	}
 939	AbstractArea getSelectionArea() { mixin(S_TRACE);
 940		auto i = _areas.getSelectionIndex();
 941		if (0 <= i) { mixin(S_TRACE);
 942			return cast(AbstractArea) _areas.getItem(i).getData();
 943		}
 944		return null;
 945	}
 946	AbstractArea[] getSelectionAreas() { mixin(S_TRACE);
 947		AbstractArea[] areas;
 948		foreach (itm; _areas.getSelection()) { mixin(S_TRACE);
 949			if (auto a = cast(AbstractArea)itm.getData()) { mixin(S_TRACE);
 950				areas ~= a;
 951			}
 952		}
 953		return areas;
 954	}
 955	void refArea(Object sender, Area a) { mixin(S_TRACE);
 956		if (_readOnly) return;
 957		if (sender is this) return;
 958		foreach (itm; _areas.getItems()) { mixin(S_TRACE);
 959			if (itm.getData() is a) refData(a, itm);
 960		}
 961	}
 962	void refBattle(Object sender, Battle a) { mixin(S_TRACE);
 963		if (_readOnly) return;
 964		if (sender is this) return;
 965		foreach (itm; _areas.getItems()) { mixin(S_TRACE);
 966			if (itm.getData() is a) refData(a, itm);
 967		}
 968	}
 969	void refPackage(Object sender, Package a) { mixin(S_TRACE);
 970		if (_readOnly) return;
 971		if (sender is this) return;
 972		foreach (itm; _areas.getItems()) { mixin(S_TRACE);
 973			if (itm.getData() is a) refData(a, itm);
 974		}
 975	}
 976	static void staticCallRefArea(AreaTable v, Commons comm, AbstractArea area) { mixin(S_TRACE);
 977		auto a = cast(Area)area;
 978		if (a) { mixin(S_TRACE);
 979			comm.refArea.call(v, a);
 980			return;
 981		}
 982		auto b = cast(Battle)area;
 983		if (b) { mixin(S_TRACE);
 984			comm.refBattle.call(v, b);
 985			return;
 986		}
 987		auto p = cast(Package)area;
 988		if (p) { mixin(S_TRACE);
 989			comm.refPackage.call(v, p);
 990			return;
 991		}
 992	}
 993	void callRefArea(AbstractArea area) { mixin(S_TRACE);
 994		if (_readOnly) return;
 995		staticCallRefArea(this, _comm, area);
 996	}
 997	void refreshIDs(bool callRef) { mixin(S_TRACE);
 998		if (_readOnly) return;
 999		if (!_areas || _areas.isDisposed()) return;
1000		if (callRef) _incSearch.close();
1001		foreach (itm; _areas.getItems()) { mixin(S_TRACE);
1002			auto area = cast(AbstractArea) itm.getData();
1003			if (!area) continue;
1004			auto str = to!(string)(area.id);
1005			if (callRef && str != itm.getText(ID)) callRefArea(area);
1006			itm.setText(ID, str);
1007		}
1008	}
1009
1010	void sort() { mixin(S_TRACE);
1011		if (_areas.getSortColumn() is null || _areas.getSortColumn() is _idSorter.column) { mixin(S_TRACE);
1012			_idSorter.doSort(_areas.getSortDirection());
1013		} else if (_areas.getSortColumn() is _nameSorter.column) { mixin(S_TRACE);
1014			_nameSorter.doSort(_areas.getSortDirection());
1015		} else if (_areas.getSortColumn() is _ucSorter.column) { mixin(S_TRACE);
1016			_ucSorter.doSort(_areas.getSortDirection());
1017		} else assert (0);
1018	}
1019
1020	@property
1021	bool showSummary() { mixin(S_TRACE);
1022		if (!_areas || _areas.isDisposed()) return false;
1023		return 0 < _areas.getItemCount() && cast(Summary)_areas.getItem(0).getData();
1024	}
1025	@property
1026	int countAllAreas() { mixin(S_TRACE);
1027		auto c = areaCount + battleCount + packageCount;
1028		if (showSummary) { mixin(S_TRACE);
1029			return 1 + c;
1030		}
1031		return c;
1032	}
1033
1034	class DragDir : DragSourceAdapter {
1035		override void dragStart(DragSourceEvent e) { mixin(S_TRACE);
1036			e.doit = false;
1037			auto tree = cast(Tree)(cast(DragSource) e.getSource()).getControl();
1038			if (!tree) return;
1039			if (!tree.isFocusControl()) return;
1040			auto sels = tree.getSelection();
1041			if (!sels.length) return;
1042			auto dir = cast(DirTree)sels[0].getData();
1043			if (!dir) return;
1044			if (!dir.parent) return;
1045			e.doit = true;
1046		}
1047		override void dragSetData(DragSourceEvent e) { mixin(S_TRACE);
1048			if (XMLBytesTransfer.getInstance().isSupportedType(e.dataType)) { mixin(S_TRACE);
1049				auto tree = cast(Tree)(cast(DragSource) e.getSource()).getControl();
1050				auto dir = cast(DirTree)tree.getSelection()[0].getData();
1051				auto doc = XNode.create("TablePath", dir.path);
1052				doc.newAttr("summaryId", _summ.id);
1053				e.data = bytesFromXML(doc.text);
1054			}
1055		}
1056		override void dragFinished(DragSourceEvent e) { mixin(S_TRACE);
1057			// ????
1058		}
1059	}
1060	class DropDir : DropTargetAdapter {
1061		private void move(DropTargetEvent e) { mixin(S_TRACE);
1062			e.detail = (!_readOnly && e.item !is null && cast(TreeItem)e.item && cast(DirTree)(cast(TreeItem)e.item).getData()) ? DND.DROP_MOVE : DND.DROP_NONE;
1063		}
1064		override void dragEnter(DropTargetEvent e){ mixin(S_TRACE);
1065			move(e);
1066		}
1067		override void dragOver(DropTargetEvent e){ mixin(S_TRACE);
1068			move(e);
1069		}
1070		override void drop(DropTargetEvent e){ mixin(S_TRACE);
1071			if (_readOnly) return;
1072			if (!isXMLBytes(e.data)) return;
1073			string xml = bytesToXML(e.data);
1074			e.detail = DND.DROP_NONE;
1075			try { mixin(S_TRACE);
1076				// ??????
1077				auto node = XNode.parse(xml);
1078				auto itm = cast(TreeItem)e.item;
1079				auto dir = cast(DirTree)itm.getData();
1080				auto path = dir.path;
1081				if (node.name == "TablePath") {
1082					if (_summ.id != AbstractArea.summaryId(node)) return;
1083					auto oldPath = node.value;
1084					auto removePath = node.value;
1085					if (oldPath == "") return;
1086					if (0 == icmp(path, oldPath)) return;
1087					auto nDir = oldPath.split("\\")[$ - 1];
1088					oldPath ~= "\\";
1089					if (istartsWith(path, oldPath)) return; // ????????????????
1090					// ?????????????
1091					foreach (sub; dir.subDirs) {
1092						if (0 == icmp(sub.name, nDir)) return;
1093					}
1094					ATUndo[] undos;
1095					void put(AbstractArea area) {
1096						if (area.name.istartsWith(oldPath)) {
1097							ulong id;
1098							TypeInfo type;
1099							getInfoFromArea(area, id, type);
1100							undos ~= new UndoEdit(this.outer, _comm, _summ, id, type);
1101							auto p = path;
1102							if (p != "") p ~= "\\";
1103							area.name = p ~ nDir ~ "\\" ~ area.name[oldPath.length .. $];
1104							callRefArea(area);
1105						}
1106					}
1107					foreach (a; _summ.areas) put(a);
1108					foreach (a; _summ.battles) put(a);
1109					foreach (a; _summ.packages) put(a);
1110
1111					if (!undos.length) {
1112						undos ~= new UndoIDs(this.outer, _comm, _summ);
1113					}
1114
1115					auto removeItm = findDirTree(removePath);
1116					auto removeDir = cast(DirTree)removeItm.getData();
1117					removeDir.parent.subDirs.remove(removeDir);
1118					new DirTree(dir, removeItm.getText());
1119					sortDirTree();
1120					refreshDirTree();
1121
1122					_undo ~= new ATUndoArr(undos);
1123				} else if (_summ.id == AbstractArea.summaryId(node)) {
1124					auto area = getSelectionArea();
1125					ulong id;
1126					TypeInfo type;
1127					getInfoFromArea(area, id, type);
1128					storeEdit(id, type);
1129					area.dirName = dir.path;
1130					callRefArea(area);
1131				} else {
1132					_dirTree.setSelection([itm]);
1133					updateDirSel();
1134					pasteImpl(node);
1135				}
1136				refreshAreas();
1137				sort();
1138				refreshStatusLine();
1139				_comm.refreshToolBar();
1140			} catch (Exception e) { mixin (S_TRACE);
1141				debugln(e);
1142			}
1143		}
1144	}
1145	class DragArea : DragSourceAdapter {
1146		AbstractArea _data;
1147		override void dragStart(DragSourceEvent e) { mixin(S_TRACE);
1148			auto tbl = cast(Table) (cast(DragSource) e.getSource()).getControl();
1149			e.doit = tbl.isFocusControl() && 0 <= tbl.getSelectionIndex() && cast(AbstractArea)tbl.getSelection()[0].getData();
1150		}
1151		override void dragSetData(DragSourceEvent e) { mixin(S_TRACE);
1152			if (XMLBytesTransfer.getInstance().isSupportedType(e.dataType)) { mixin(S_TRACE);
1153				auto tbl = cast(Table) (cast(DragSource) e.getSource()).getControl();
1154				int i = tbl.getSelectionIndex();
1155				assert (0 <= i);
1156				_data = cast(AbstractArea)tbl.getItem(i).getData();
1157				assert (_data !is null);
1158				e.data = bytesFromXML(_data.toXML(new XMLOption(_prop.sys), _summ.id));
1159			}
1160		}
1161		override void dragFinished(DragSourceEvent e) { mixin(S_TRACE);
1162			if (_readOnly) return;
1163			if (e.detail == DND.DROP_MOVE) { mixin(S_TRACE);
1164				auto area = _data;
1165				_summ.remove(area);
1166				delItem(area);
1167				if (cast(Area) area) { mixin(S_TRACE);
1168					_comm.delArea.call(cast(Area) area);
1169				} else if (cast(Battle) area) { mixin(S_TRACE);
1170					_comm.delBattle.call(cast(Battle) area);
1171				} else { mixin(S_TRACE);
1172					assert (cast(Package) area);
1173					_comm.delPackage.call(cast(Package) area);
1174				}
1175				refreshStatusLine();
1176				_comm.refreshToolBar();
1177			}
1178		}
1179	}
1180	class DropArea : DropTargetAdapter {
1181		override void dragEnter(DropTargetEvent e){ mixin(S_TRACE);
1182			e.detail = !_readOnly && _areas.getItemCount() == countAllAreas ? DND.DROP_MOVE : DND.DROP_NONE;
1183		}
1184		override void dragOver(DropTargetEvent e){ mixin(S_TRACE);
1185			e.detail = !_readOnly && _areas.getItemCount() == countAllAreas ? DND.DROP_MOVE : DND.DROP_NONE;
1186		}
1187		override void drop(DropTargetEvent e){ mixin(S_TRACE);
1188			if (_readOnly) return;
1189			if (!isXMLBytes(e.data)) return;
1190			e.detail = DND.DROP_NONE;
1191			string xml = bytesToXML(e.data);
1192			try { mixin(S_TRACE);
1193				scope node = XNode.parse(xml);
1194				TypeInfo tid;
1195				if (node.name == Area.XML_NAME) { mixin(S_TRACE);
1196					tid = typeid(Area);
1197				} else if (node.name == Battle.XML_NAME) { mixin(S_TRACE);
1198					tid = typeid(Battle);
1199				} else if (node.name == Package.XML_NAME) { mixin(S_TRACE);
1200					tid = typeid(Package);
1201				} else { mixin(S_TRACE);
1202					return;
1203				}
1204
1205				bool sortedID = _areas.getSortColumn() is null || _areas.getSortColumn() is _idSorter.column;
1206				auto tbl = cast(Table) (cast(DropTarget) e.getSource()).getControl();
1207				auto toItm = tbl.getItem(tbl.toControl(e.x, e.y));
1208				Object toData = toItm ? toItm.getData() : null;
1209				int getIndex(AbstractArea area) { mixin(S_TRACE);
1210					int index;
1211					if (auto a = cast(Area)area) { mixin(S_TRACE);
1212						if (toItm && cast(Summary)toData) { mixin(S_TRACE);
1213							index = 0;
1214						} else if (!toItm || cast(Battle)toData || cast(Package)toData) { mixin(S_TRACE);
1215							index = _summ.areas.length;
1216						} else { mixin(S_TRACE);
1217							index = _summ.indexOf(cast(Area)toData);
1218						}
1219					} else if (auto a = cast(Battle)area) { mixin(S_TRACE);
1220						if (toItm && (cast(Summary)toData || cast(Area)toData)) { mixin(S_TRACE);
1221							index = 0;
1222						} else if (!toItm || cast(Package)toData) { mixin(S_TRACE);
1223							index = _summ.battles.length;
1224						} else { mixin(S_TRACE);
1225							index = _summ.indexOf(cast(Battle)toData);
1226						}
1227					} else if (auto a = cast(Package)area) { mixin(S_TRACE);
1228						if (toItm && (cast(Summary)toData || cast(Area)toData || cast(Battle)toData)) { mixin(S_TRACE);
1229							index = 0;
1230						} else if (!toItm) { mixin(S_TRACE);
1231							index = _summ.packages.length;
1232						} else { mixin(S_TRACE);
1233							index = _summ.indexOf(cast(Package)toData);
1234						}
1235					} else assert (0);
1236					return index;
1237				}
1238				if (_summ.id == AbstractArea.summaryId(node)) { mixin(S_TRACE);
1239					// ?????????
1240					if (!sortedID) return;
1241					int fromIndex = tbl.getSelectionIndex();
1242					if (showSummary && fromIndex < 1) return;
1243
1244					auto area = cast(AbstractArea)tbl.getItem(fromIndex).getData();
1245					int index = getIndex(area);
1246					void put(A)(A a) { mixin(S_TRACE);
1247						int moveIndex = _summ.indexOf(a);
1248						if (moveIndex == index) return;
1249						storeMove(moveIndex, index, typeid(typeof(a)));
1250						_summ.insert(index, a);
1251					}
1252					if (auto a = cast(Area)area) { mixin(S_TRACE);
1253						put(a);
1254					} else if (auto a = cast(Battle)area) { mixin(S_TRACE);
1255						put(a);
1256					} else if (auto a = cast(Package)area) { mixin(S_TRACE);
1257						put(a);
1258					} else assert (0);
1259					callRefArea(area);
1260					refreshAreas();
1261					sort();
1262					select(area);
1263					refreshStatusLine();
1264					_comm.refreshToolBar();
1265					e.detail = DND.DROP_NONE;
1266				} else { mixin(S_TRACE);
1267					// ???????????
1268					AbstractArea area;
1269					auto ver = new XMLInfo(_prop.sys, LATEST_VERSION);
1270					ulong[] a, b, p;
1271					saveIDs(_summ, a, b, p);
1272					if (tid == typeid(Area)) { mixin(S_TRACE);
1273						area = Area.createFromNode(node, ver);
1274						if (_dirMode) area.dirName = _dir;
1275						int index = getIndex(area);
1276						_summ.insert(index, cast(Area) area);
1277						storeInsert(area.id, tid, a, b, p);
1278						index = _summ.indexOf(cast(Area) area);
1279						newAreaItem(index);
1280					} else if (tid == typeid(Battle)) { mixin(S_TRACE);
1281						area = Battle.createFromNode(node, ver);
1282						if (_dirMode) area.dirName = _dir;
1283						int index = getIndex(area);
1284						_summ.insert(index, cast(Battle) area);
1285						storeInsert(area.id, tid, a, b, p);
1286						index = _summ.indexOf(cast(Battle) area);
1287						newBattleItem(index);
1288					} else { mixin(S_TRACE);
1289						assert (tid == typeid(Package));
1290						area = Package.createFromNode(node, ver);
1291						if (_dirMode) area.dirName = _dir;
1292						int index = getIndex(area);
1293						_summ.insert(index, cast(Package) area);
1294						storeInsert(area.id, tid, a, b, p);
1295						index = _summ.indexOf(cast(Package) area);
1296						newPackageItem(index);
1297					}
1298					e.detail = DND.DROP_NONE;
1299					refreshIDs(true);
1300					sort();
1301					select(area);
1302					_comm.refUseCount.call();
1303					refreshStatusLine();
1304					_comm.refreshToolBar();
1305				}
1306			} catch (Exception e) { mixin (S_TRACE);
1307				debugln(e);
1308			}
1309		}
1310	}
1311	int indexOf(in AbstractArea area) { mixin(S_TRACE);
1312		foreach (i, itm; _areas.getItems()) { mixin(S_TRACE);
1313			if (itm.getData() is area) { mixin(S_TRACE);
1314				return i;
1315			}
1316		}
1317		return -1;
1318	}
1319	void __refreshUseCount() { mixin(S_TRACE);
1320		foreach (itm; _areas.getItems()) { mixin(S_TRACE);
1321			auto element = itm.getData();
1322			if (cast(Area) element) { mixin(S_TRACE);
1323				itm.setText(2, to!(string)(_summ.useCounter.area.get(toAreaId((cast(AbstractArea) element).id))));
1324			} else if (cast(Battle) element) { mixin(S_TRACE);
1325				itm.setText(2, to!(string)(_summ.useCounter.battle.get(toBattleId((cast(AbstractArea) element).id))));
1326			} else if (cast(Package) element) { mixin(S_TRACE);
1327				itm.setText(2, to!(string)(_summ.useCounter.packages.get(toPackageId((cast(AbstractArea) element).id))));
1328			}
1329		}
1330	}
1331	class TreeMListener : MouseAdapter {
1332		public override void mouseDoubleClick(MouseEvent e) { mixin(S_TRACE);
1333			if (_dirTree.isFocusControl() && e.button == 1) { mixin(S_TRACE);
1334				auto itm = _dirTree.getItem(new Point(e.x, e.y));
1335				if (itm && cast(Summary)itm.getData()) { mixin(S_TRACE);
1336					editSummary();
1337				}
1338			}
1339		}
1340	}
1341	class TreeKListener : KeyAdapter {
1342		public override void keyPressed(KeyEvent e) { mixin(S_TRACE);
1343			if (_dirTree.isFocusControl() && e.character == SWT.CR) { mixin(S_TRACE);
1344				auto sels = _dirTree.getSelection();
1345				if (!sels.length) return;
1346				auto sel = sels[0];
1347				if (cast(Summary)sel.getData()) { mixin(S_TRACE);
1348					editSummary();
1349				}
1350			}
1351		}
1352	}
1353	class MListener : MouseAdapter {
1354		public override void mouseUp(MouseEvent e) { mixin(S_TRACE);
1355			if (e.button == 2) { mixin(S_TRACE);
1356				auto itm = _areas.getItem(new Point(e.x, e.y));
1357				if (itm && cast(Summary)itm.getData()) { mixin(S_TRACE);
1358					editSummary();
1359				} else { mixin(S_TRACE);
1360					if (_prop.var.etc.clickIsOpenEvent) { mixin(S_TRACE);
1361						openAreaScene(e.x, e.y, true);
1362					} else { mixin(S_TRACE);
1363						openAreaEvent(e.x, e.y, true);
1364					}
1365				}
1366			}
1367		}
1368		public override void mouseDoubleClick(MouseEvent e) { mixin(S_TRACE);
1369			if (_areas.isFocusControl() && e.button == 1) { mixin(S_TRACE);
1370				auto itm = _areas.getItem(new Point(e.x, e.y));
1371				if (itm && cast(Summary)itm.getData()) { mixin(S_TRACE);
1372					editSummary();
1373				} else { mixin(S_TRACE);
1374					bool shift = 0 != (e.stateMask & SWT.SHIFT);
1375					if (_prop.var.etc.clickIsOpenEvent) shift = !shift;
1376					if (shift) { mixin(S_TRACE);
1377						openAreaEvent(true);
1378					} else { mixin(S_TRACE);
1379						openAreaScene(true);
1380					}
1381				}
1382			}
1383		}
1384	}
1385	class KListener : KeyAdapter {
1386		public override void keyPressed(KeyEvent e) { mixin(S_TRACE);
1387			if (_areas.isFocusControl() && e.character == SWT.CR) { mixin(S_TRACE);
1388				auto sel = _areas.getSelectionIndex();
1389				if (sel == -1) return;
1390				if (cast(Summary)_areas.getItem(sel).getData()) { mixin(S_TRACE);
1391					editSummary();
1392				} else { mixin(S_TRACE);
1393					bool shift = 0 != (e.stateMask & SWT.SHIFT);
1394					if (_prop.var.etc.clickIsOpenEvent) shift = !shift;
1395					if (shift) { mixin(S_TRACE);
1396						openAreaEvent(true);
1397					} else { mixin(S_TRACE);
1398						openAreaScene(true);
1399					}
1400				}
1401			}
1402		}
1403	}
1404	class ADListener : DisposeListener {
1405		override void widgetDisposed(DisposeEvent e) { mixin(S_TRACE);
1406			_dirs = null;
1407			_dir = "";
1408			_areas = null;
1409			_dirTree = null;
1410			if (!_readOnly) {
1411				_comm.refArea.remove(&refArea);
1412				_comm.refBattle.remove(&refBattle);
1413				_comm.refPackage.remove(&refPackage);
1414				_comm.refUseCount.remove(&__refreshUseCount);
1415				_comm.replText.remove(&replText);
1416				_comm.replText.remove(&refresh);
1417				_comm.refScenario.remove(&refScenario);
1418				_comm.refScenarioName.remove(&refScenarioName);
1419				_comm.refUndoMax.remove(&refUndoMax);
1420			}
1421			_comm.refAreaTable.remove(&refreshAreas);
1422		}
1423	}
1424	static class DirTree {
1425		DirTree parent;
1426		string name;
1427		DirTree[] subDirs;
1428		this (DirTree parent, string name) { mixin (S_TRACE);
1429			this.parent = parent;
1430			this.name = name;
1431			if (parent) { mixin (S_TRACE);
1432				parent.subDirs ~= this;
1433			}
1434		}
1435		@property
1436		const
1437		string path() { mixin (S_TRACE);
1438			if (!parent) return name;
1439			auto path = parent.path;
1440			return path == "" ? name : path ~ "\\" ~ name;
1441		}
1442		@property
1443		const
1444		DirTree dup() { mixin (S_TRACE);
1445			DirTree recurse(DirTree parent, in DirTree dir) { mixin (S_TRACE);
1446				auto dir2 = new DirTree(parent, dir.name);
1447				foreach (sub; dir.subDirs) { mixin (S_TRACE);
1448					recurse(dir2, sub);
1449				}
1450				return dir2;
1451			}
1452			return recurse(null, this);
1453		}
1454	}
1455	private void delDirTree(string path) {
1456		if (_readOnly) return;
1457		if (path == "") return;
1458		auto itm = findDirTree(path);
1459		auto dir = cast(DirTree)itm.getData();
1460		assert (dir !is null);
1461		assert (dir.parent !is null);
1462		dir.parent.subDirs.remove(dir);
1463		itm.dispose();
1464	}
1465	private void replText() {
1466		if (_readOnly) return;
1467		constructDirTree(false);
1468	}
1469	private void constructDirTree(bool add) { mixin(S_TRACE);
1470		string[] stored;
1471		if (add && _dirs) { mixin(S_TRACE);
1472			void store(DirTree dir) { mixin(S_TRACE);
1473				stored ~= dir.path;
1474				foreach (sub; dir.subDirs) store(sub);
1475			}
1476			store(_dirs);
1477		}
1478		_dirs = new DirTree(null, "");
1479		DirTree[string] itmTable;
1480		itmTable[""] = _dirs;
1481		void put(string dirName) { mixin (S_TRACE);
1482			auto dirs = .split(dirName, "\\");
1483			auto itm = _dirs;
1484			foreach (i, dir; dirs) { mixin (S_TRACE);
1485				auto fPath = dirs[0 .. i + 1].join("\\");
1486				auto path = fPath.toLower();
1487				auto p = path in itmTable;
1488				if (p) { mixin (S_TRACE);
1489					itm = *p;
1490				} else { mixin (S_TRACE);
1491					auto sub = new DirTree(itm, dir);
1492					itm = sub;
1493					itmTable[path] = sub;
1494				}
1495			}
1496		}
1497		foreach (a; _summ.areas) put(a.dirName);
1498		foreach (a; _summ.battles) put(a.dirName);
1499		foreach (a; _summ.packages) put(a.dirName);
1500		foreach (s; stored) put(s);
1501
1502		sortDirTree();
1503	}
1504	private string putDir(DirTree parent, string dirName) { mixin (S_TRACE);
1505		auto dirs = .split(dirName, "\\");
1506		foreach (i, dir; dirs) { mixin (S_TRACE);
1507			bool exists = false;
1508			foreach (sub; parent.subDirs) { mixin (S_TRACE);
1509				if (0 == icmp(sub.name, dir)) { mixin (S_TRACE);
1510					parent = sub;
1511					exists = true;
1512					break;
1513				}
1514			}
1515			if (!exists) {
1516				parent = new DirTree(parent, dir);
1517			}
1518		}
1519		return parent.path;
1520	}
1521	private void sortDirTree() {
1522		void recurse(DirTree dir) { mixin(S_TRACE);
1523			if (_prop.var.etc.logicalSort) { mixin(S_TRACE);
1524				.sortDlg(dir.subDirs, (DirTree a, DirTree b) => incmp(a.name, b.name) < 0);
1525			} else { mixin(S_TRACE);
1526				.sortDlg(dir.subDirs, (DirTree a, DirTree b) => icmp(a.name, b.name) < 0);
1527			}
1528			foreach (sub; dir.subDirs) recurse(sub);
1529		}
1530		recurse(_dirs);
1531	}
1532	private TreeItem findDirTree(string path) { mixin(S_TRACE);
1533		auto dirs = .split(path, "\\");
1534		auto itm = rootDir;
1535		foreach (i, dir; dirs) { mixin(S_TRACE);
1536			foreach (sub; itm.getItems()) { mixin(S_TRACE);
1537				auto ds = cast(DirTree)sub.getData();
1538				if (0 == icmp(ds.name, dir)) { mixin(S_TRACE);
1539					itm = sub;
1540					break;
1541				}
1542			}
1543		}
1544		return itm;
1545	}
1546	private void refreshDirTree() { mixin(S_TRACE);
1547		if (!_dirTree) return;
1548		if (_areaDirEdit) _areaDirEdit.cancel();
1549		bool selSummary = false;
1550		auto sel = "";
1551		auto sels = _dirTree.getSelection();
1552		if (sels.length) { mixin (S_TRACE);
1553			auto path = cast(DirTree)sels[0].getData();
1554			if (path) { mixin (S_TRACE);
1555				sel = path.path;
1556			} else { mixin (S_TRACE);
1557				assert (cast(Summary)sels[0].getData() !is null);
1558				selSummary = true;
1559			}
1560		}
1561		_dirTree.removeAll();
1562		if (!_summ) return;
1563		if (_prop.var.etc.showSummaryInAreaTable) { mixin (S_TRACE);
1564			auto itm = new TreeItem(_dirTree, SWT.NONE);
1565			itm.setImage(_prop.images.summary);
1566			itm.setText(_summ.scenarioName);
1567			itm.setData(_summ);
1568			if (selSummary) { mixin (S_TRACE);
1569				_dirTree.setSelection([itm]);
1570			}
1571		}
1572
1573		void recurse(T)(T tree, DirTree dir) { mixin (S_TRACE);
1574			auto itm = new TreeItem(tree, SWT.NONE);
1575			static if (is(T:Tree)) {
1576				itm.setText(_prop.msgs.areaDirRoot);
1577			} else {
1578				itm.setText(dir.name);
1579			}
1580			itm.setImage(_prop.images.areaDir);
1581			itm.setData(dir);
1582			if (!selSummary && 0 == icmp(sel, dir.path)) { mixin (S_TRACE);
1583				_dirTree.setSelection([itm]);
1584			} else if (!_dirTree.getSelection().length) { mixin (S_TRACE);
1585				_dirTree.setSelection([itm]);
1586			}
1587			foreach (sub; dir.subDirs) { mixin (S_TRACE);
1588				recurse(itm, sub);
1589			}
1590		}
1591		recurse(_dirTree, _dirs);
1592
1593		_dirTree.treeExpandedAll();
1594		_dirTree.showSelection();
1595	}
1596	private Image areaImage(in Area a) {
1597		return _summ.startArea == a.id ? _prop.images.startArea : _prop.images.area;
1598	}
1599	private void updateDirSel() { mixin(S_TRACE);
1600		auto sels = _dirTree.getSelection();
1601		if (!sels.length) return;
1602		auto sel = cast(DirTree)sels[0].getData();
1603		if (!sel) return;
1604		_dir = sel.path;
1605		refreshAreasImpl(true);
1606	}
1607	private void refreshAreas() { mixin(S_TRACE);
1608		refreshAreasImpl(false);
1609	}
1610	private void refreshAreasImpl(bool selDir) { mixin(S_TRACE);
1611		if (!_areas || _areas.isDisposed()) return;
1612		_parent.setRedraw(false);
1613		scope (exit) _parent.setRedraw(true);
1614		if (!selDir) refreshDirTree();
1615		int topIndex = _areas.getTopIndex();
1616		auto sel = _areas.getSelectionIndex();
1617		if (_summ) { mixin(S_TRACE);
1618			size_t i = 0;
1619			if (!_dirMode && _prop.var.etc.showSummaryInAreaTable) { mixin(S_TRACE);
1620				refSummary();
1621				i++;
1622			}
1623			void put(AbstractArea a) { mixin(S_TRACE);
1624				if (_dirMode) { mixin(S_TRACE);
1625					if (0 != icmp(a.dirName, _dir)) return;
1626					if (!_incSearch.match(a.baseName, a)) return;
1627				} else { mixin(S_TRACE);
1628					if (!_incSearch.match(a.name, a)) return;
1629				}
1630				refData2(a, i < _areas.getItemCount() ? _areas.getItem(i) : new TableItem(_areas, SWT.NONE));
1631				i++;
1632			}
1633			foreach (a; _summ.areas) { mixin(S_TRACE);
1634				put(a);
1635			}
1636			foreach (a; _summ.battles) { mixin(S_TRACE);
1637				put(a);
1638			}
1639			foreach (a; _summ.packages) { mixin(S_TRACE);
1640				put(a);
1641			}
1642			while (i < _areas.getItemCount()) { mixin(S_TRACE);
1643				_areas.getItem(i).dispose();
1644			}
1645			sort();
1646		} else { mixin(S_TRACE);
1647			_areas.removeAll();
1648		}
1649		_areas.setTopIndex(topIndex);
1650		if (sel < 0 || _areas.getItemCount() <= sel) { mixin(S_TRACE);
1651			_areas.select(.min(_areas.getItemCount() - 1, sel));
1652		}
1653		_areas.showSelection();
1654		refreshStatusLine();
1655		_comm.refreshToolBar();
1656	}
1657	void refSummary() { mixin(S_TRACE);
1658		if (!_summ) return;
1659		TableItem itm;
1660		if (_areas.getItemCount()) { mixin(S_TRACE);
1661			if (!showSummary) return;
1662			itm = _areas.getItem(0);
1663		} else { mixin(S_TRACE);
1664			itm = new TableItem(_areas, SWT.NONE, 0);
1665		}
1666		itm.setImage(0, _prop.images.summary);
1667		itm.setText(ID, "-");
1668		itm.setText(NA

Large files files are truncated, but you can click here to view the full file