text: #include "ide.h"
  
  class TextOption : public Option {
  public:
  	virtual void   SetData(const Value& data);
  	virtual Value  GetData() const;
  };
  
  void  TextOption::SetData(const Value& data)
  {
  	String s = data;
  	Set(!(IsNull(s) || s == "0"));
  }
  
  Value TextOption::GetData() const
  {
  	return Get() ? "1" : "0";
  }
  
  class TextSwitch : public Switch {
  public:
  	virtual void   SetData(const Value& data);
  	virtual Value  GetData() const;
  };
  
  void  TextSwitch::SetData(const Value& data)
  {
  	String s = data;
  	Switch::SetData(atoi((String)data));
  }
  
  Value TextSwitch::GetData() const
  {
  	return AsString(Switch::GetData());
  }
  
  class DirTable : public ArrayCtrl {
  public:
  	virtual void   SetData(const Value& data);
  	virtual Value  GetData() const;
  
  protected:
  	void Modify()  { Update(); }
  
  	EditString      edit;
  	SelectDirButton edit_dir;
  
  	void Init(const char *name = NULL);
  
  public:
  	DirTable();
  	DirTable(const char *name);
  };
  
  void   DirTable::SetData(const Value& data)
  {
  	Vector<String> l = Split((String)data, ';');
  	Clear();
  	for(int i = 0; i < l.GetCount(); i++)
  		Add(l[i]);
  }
  
  Value  DirTable::GetData() const
  {
  	String s;
  	for(int i = 0; i < GetCount(); i++) {
  		if(i) s << ';';
  		s << (String)Get(i, 0);
  	}
  	return s;
  }
  
  void DirTable::Init(const char *name)
  {
  	AutoHideSb();
  	AddColumn(name).Edit(edit);
  	Appending().Removing().Moving();
  	edit_dir.Attach(edit);
  	edit_dir.AllFilesType();
  	WhenArrayAction = edit <<= callback(this, &DirTable::Modify);
  }
  
  DirTable::DirTable()
  {
  	Init();
  	NoHeader();
  }
  
  DirTable::DirTable(const char *name)
  {
  	Init(name);
  }
  
  class DirMap : public ArrayCtrl {
  public:
  	virtual void   SetData(const Value& data);
  	virtual Value  GetData() const;
  
  protected:
  	void Modify()  { Update(); }
  
  	EditString      localpath, remotepath;
  	SelectDirButton edit_dir;
  
  public:
  	DirMap();
  };
  
  void DirMap::SetData(const Value& data)
  {
  	Vector<String> l = Split((String)data, ';');
  	Clear();
  	for(int i = 0; i < l.GetCount(); i++) {
  		String li = l[i];
  		int f = li.Find('>');
  		if(f >= 0)
  			Add(li.Left(f), li.Mid(f + 1));
  	}
  }
  
  Value DirMap::GetData() const
  {
  	String s;
  	for(int i = 0; i < GetCount(); i++) {
  		if(i) s << ';';
  		s << (String)Get(i, 0) << '>' << (String)Get(i, 1);
  	}
  	return s;
  }
  
  DirMap::DirMap()
  {
  	AutoHideSb();
  	AddColumn("Map local path").Edit(localpath);
  	AddColumn("To remote path").Edit(remotepath);
  	Appending().Removing().Moving();
  	edit_dir.Attach(localpath);
  	WhenArrayAction = localpath <<= remotepath <<= callback(this, &DirMap::Modify);
  }
  
  struct BuildMethods : public WithBuildMethodsLayout<TopWindow>
  {
  	TextOption debug_blitz;
  	TextSwitch debug_linkmode;
  	TextOption release_blitz;
  	TextSwitch release_linkmode;
  	TextOption linkmode_lock;
  	DirTable   path;
  	DirTable   include;
  	DirTable   lib;
  	DirMap     remote_path_map;
  	OpenFileButton open_script;
  
  	EditStringNotNull name;
  	Index<String>     origfile;
  	String            default_method;
  
  	void Load();
  	bool Save();
  
  	void NewBuilder();
  	void ShowDefault();
  	void SetDefault();
  	void ChangeMethod();
  	void Import();
  
  	void MethodMenu(Bar& bar);
  
  	typedef BuildMethods CLASSNAME;
  
  	BuildMethods();
  };
  
  int CharFilterFileName(int c)
  {
  	return IsAlNum(c) || c == '_' ? c : 0;
  }
  
  BuildMethods::BuildMethods()
  {
  	CtrlLayoutOKCancel(*this, "Build methods");
  	method.AddColumn("Method").Edit(name);
  	name.SetFilter(CharFilterFileName);
  	method.AddCtrl("BUILDER", builder);
  	method.AddCtrl("COMPILER", compiler);
  	method.AddCtrl("DEBUG_INFO", debug_info);
  	method.AddCtrl("DEBUG_BLITZ", debug_blitz);
  	method.AddCtrl("DEBUG_LINKMODE", debug_linkmode);
  	method.AddCtrl("DEBUG_OPTIONS", debug_options);
  	method.AddCtrl("DEBUG_FLAGS", debug_flags);
  	method.AddCtrl("DEBUG_LINK", debug_link);
  	method.AddCtrl("RELEASE_BLITZ", release_blitz);
  	method.AddCtrl("RELEASE_LINKMODE", release_linkmode);
  	method.AddCtrl("RELEASE_OPTIONS", speed_options);
  	method.AddCtrl("RELEASE_SIZE_OPTIONS", size_options);
  	method.AddCtrl("RELEASE_FLAGS", release_flags);
  	method.AddCtrl("RELEASE_LINK", release_link);
  	method.AddCtrl("DEBUGGER", debugger);
  	method.AddCtrl("PATH", path);
  	method.AddCtrl("INCLUDE", include);
  	method.AddCtrl("LIB", lib);
  #if 0 // REMOTE REMOVED
  	method.AddCtrl("REMOTE_HOST", remote_host);
  	method.AddCtrl("REMOTE_OS", remote_os);
  	remote_os.Add("WIN32");
  	remote_os.Add("LINUX");
  	remote_os.Add("WINCE");
  	remote_os.Add("UNIX");
  	remote_os.Add("SOLARIS");
  	remote_os.Add("BSD");
  	method.AddCtrl("REMOTE_TRANSFER", remote_file_access);
  	remote_file_access.Add("0", "direct (SAMBA)");
  	remote_file_access.Add("1", "indirect (transfer)");
  	method.AddCtrl("REMOTE_MAP", remote_path_map);
  #endif
  	method.AddCtrl("SCRIPT", scriptfile);
  	method.AddCtrl("LINKMODE_LOCK", linkmode_lock);
  	open_script.Attach(scriptfile);
  	open_script.Type("Build scripts (*.bsc)", "*.bsc")
  		.AllFilesType();
  	open_script.DefaultExt("bsc");
  	method.Appending().Removing().Duplicating();
  	method.WhenCursor = THISBACK(ChangeMethod);
  	method.WhenBar = THISBACK(MethodMenu);
  
  	paths.Add(path.SizePos(), "PATH - executable directories");
  	paths.Add(include.SizePos(), "INCLUDE directories");
  	paths.Add(lib.SizePos(), "LIB directories");
  
  	debug_info.Add("0", "None");
  	debug_info.Add("1", "Minimal");
  	debug_info.Add("2", "Full");
  
  	for(int i = 0; i < BuilderMap().GetCount(); i++)
  		builder.Add(BuilderMap().GetKey(i));
  
  	builder <<= THISBACK(NewBuilder);
  	setdefault <<= THISBACK(SetDefault);
  
  	linkmode_lock.SetLabel("Lock link mode");
  }
  
  void BuildMethods::MethodMenu(Bar& bar)
  {
  	method.StdBar(bar);
  	bar.Separator();
  	bar.Add("Import", THISBACK(Import));
  }
  
  void BuildMethods::Import()
  {
  	if(!Save())
  		return;
  	FileSel fsel;
  	fsel.Type("Build methods (*.bm)", "*.bm")
  		.AllFilesType()
  		.Multi()
  		.DefaultExt("bm");
  	if(!fsel.ExecuteOpen())
  		return;
  	for(int i = 0; i < fsel.GetCount(); i++) {
  		String f = LoadFile(fsel[i]);
  		if(f.IsVoid()) {
  			if(!PromptOKCancel(NFormat("Failed to load [* \1%s\1]. Continue?", fsel[i])))
  				break;
  			continue;
  		}
  		String nf = ConfigFile(GetFileNamePos(fsel[i]));
  		if(FileExists(nf) && !PromptOKCancel(NFormat("File already exists: [* \1%s\1]. Overwrite?", nf)))
  			continue;
  		if(!SaveFile(nf, f))
  			if(!PromptOKCancel(NFormat("Failed to save [* \1%s\1]. Continue?", nf)))
  				break;
  	}
  	Load();
  }
  
  static int sCompare(const Value& v1, const Value& v2)
  {
  	return (String)v1 < (String)v2;
  }
  
  void BuildMethods::NewBuilder()
  {
  	String b = ~builder;
  	bool gcc = b == "GCC" || b == "GCC32" || b == "GCC_ARM";
  	if(IsNull(speed_options))
  		if(gcc)
  			speed_options <<= "-O3 -ffunction-sections -fdata-sections";
  		else
  			speed_options <<= "-O2";
  	if(IsNull(size_options))
  		if(gcc)
  			size_options <<= "-Os -finline-limit=20 -ffunction-sections -fdata-sections";
  		else
  			size_options <<= "-O1";
  	if(IsNull(debug_options))
  		if(gcc)
  			debug_options <<= "-O0";
  		else
  			debug_options <<= "-Od";
  	if(IsNull(debugger))
  		if(gcc)
  			debugger <<= "gdb";
  		else
  			debugger <<= "msdev";
  	if(IsNull(release_link) && gcc)
  		release_link <<= "-Wl,--gc-sections";
  	ChangeMethod();
  }
  
  void BuildMethods::ChangeMethod()
  {
  	String b;
  	if(method.IsCursor())
  		b = method.Get("BUILDER");
  	scriptfile.Enable(b == "SCRIPT");
  }
  
  void BuildMethods::Load()
  {
  	FindFile ff(ConfigFile("*.bm"));
  	while(ff) {
  		VectorMap<String, String> map;
  		String fn = ConfigFile(ff.GetName());
  		if(LoadVarFile(fn, map)) {
  			origfile.Add(fn);
  			method.Add(GetFileTitle(fn));
  			for(int j = 1; j < method.GetIndexCount(); j++)
  				method.Set(method.GetCount() - 1, j, map.Get(method.GetId(j).ToString(), Null));
  		}
  		ff.Next();
  	}
  	method.Sort(0, sCompare);
  	method.GoBegin();
  }
  
  bool BuildMethods::Save()
  {
  	int i;
  	Index<String> name;
  	for(i = 0; i < method.GetCount(); i++) {
  		String n = method.Get(i, 0);
  		if(name.Find(n) >= 0) {
  			Exclamation("Duplicate method [* " + DeQtf(n) + "] !");
  			return false;
  		}
  		name.Add(n);
  	}
  	Index<String> saved;
  	for(i = 0; i < method.GetCount(); i++) {
  		VectorMap<String, String> map;
  		for(int j = 1; j < method.GetIndexCount(); j++)
  			map.Add(method.GetId(j).ToString(), method.Get(i, j));
  		if(map.Get("BUILDER", "") != "SCRIPT")
  			map.RemoveKey("SCRIPT");
  		String fn = ConfigFile(String(method.Get(i, 0)) + ".bm");
  		if(!SaveVarFile(fn, map)) {
  			Exclamation("Error saving [* " + fn + "] !");
  			return false;
  		}
  		saved.Add(fn);
  	}
  	for(i = 0; i < origfile.GetCount(); i++)
  		if(saved.Find(origfile[i]) < 0)
  			DeleteFile(origfile[i]);
  	return true;
  }
  
  struct BoldDisplay : Display {
  	virtual void Paint(Draw& w, const Rect& r, const Value& q,
  					   Color ink, Color paper, dword style) const {
  		w.DrawRect(r, paper);
  		DrawSmartText(w, r.left, r.top, r.Width(), (String)q, StdFont().Bold(), ink);
  	}
  };
  
  
  void BuildMethods::ShowDefault()
  {
  	String m = GetDefaultMethod();
  	for(int i = 0; i < method.GetCount(); i++)
  		if((String)method.Get(i, 0) == m)
  			method.SetDisplay(i, 0, Single<BoldDisplay>());
  		else
  			method.SetDisplay(i, 0, StdDisplay());
  }
  
  void BuildMethods::SetDefault()
  {
  	if(method.IsCursor()) {
  		SaveFile(ConfigFile("default_method"), method.GetKey());
  		ShowDefault();
  	}
  }
  
  void Ide::SetupBuildMethods()
  {
  	BuildMethods m;
  	m.Load();
  	m.ShowDefault();
  	m.use_target = use_target;
  	for(;;) {
  		int c = m.Run();
  		if(c == IDCANCEL)
  			break;
  		if(c == IDOK && m.Save()) {
  			use_target = m.use_target;
  			break;
  		}
  	}
  	SyncBuildMode();
  	SetBar();
  }
  