void
JFSBindingList::SetCommand
	(
	const JCharacter*				pattern,
	const JCharacter*				cmd,
	const JFSBinding::CommandType	type,
	const JBoolean					singleFile
	)
{
	JFSBinding* b = new JFSBinding(pattern, cmd, type, singleFile, kJFalse);
	assert( b != NULL );

	JIndex index;
	if (itsBindingList->SearchSorted(b, JOrderedSetT::kLastMatch, &index) &&
		!(itsBindingList->NthElement(index))->IsSystemBinding())
		{
		delete b;
		b = itsBindingList->NthElement(index);
		b->SetCommand(cmd, type, singleFile);
		}
	else
		{
		AddBinding(b);
		}
}
JBoolean
JFSBindingList::SetCommand
	(
	const JIndex		index,
	const JCharacter*	cmd
	)
{
	JFSBinding* b = itsBindingList->NthElement(index);

	JFSBinding::CommandType type;
	JBoolean singleFile;
	if (b->GetCommand(&type, &singleFile) == cmd)
		{
		return kJFalse;
		}

	if (b->IsSystemBinding())
		{
		const JString pattern = b->GetPattern();		// in case b is deleted
		AddBinding(pattern, cmd, type, singleFile);		// move b to itsOverriddenList
		}
	else
		{
		b->SetCommand(cmd, type, singleFile);
		}

	return kJTrue;
}
JBoolean
JFSBindingList::DeleteBinding
	(
	const JIndex index
	)
{
	JFSBinding* b = itsBindingList->NthElement(index);
	if (b->IsSystemBinding())
		{
		return kJTrue;
		}

	JIndex fIndex;
	const JBoolean found =
		itsOverriddenList->SearchSorted(b, JOrderedSetT::kLastMatch, &fIndex);

	itsBindingList->DeleteElement(index);
	b = NULL;

	if (found)
		{
		b = itsOverriddenList->NthElement(fIndex);
		itsOverriddenList->RemoveElement(fIndex);

		const JBoolean inserted = itsBindingList->InsertSorted(b, kJFalse);
		assert( inserted );

		return kJFalse;
		}
	else
		{
		return kJTrue;
		}
}
JBoolean
JFSBindingList::SetPattern
	(
	const JIndex		index,
	const JCharacter*	pattern,
	JIndex*				newIndex
	)
{
	*newIndex = index;

	JFSBinding* b = itsBindingList->NthElement(index);
	if (b->GetPattern() == pattern)
		{
		return kJTrue;
		}

	JFSBinding temp(pattern, "", JFSBinding::kRunPlain, kJFalse, kJFalse);
	JIndex fIndex;
	if (itsBindingList->SearchSorted(&temp, JOrderedSetT::kAnyMatch, &fIndex))
		{
		return kJFalse;
		}

	if (b->IsSystemBinding())
		{
		JFSBinding::CommandType type;
		JBoolean singleFile;
		const JString cmd = b->GetCommand(&type, &singleFile);	// in case b is deleted
		AddBinding(pattern, cmd, type, singleFile);				// move b to itsOverriddenList
		b = &temp;
		}
	else
		{
		b->SetPattern(pattern);
		itsBindingList->Sort();
		}

	const JBoolean found =
		itsBindingList->SearchSorted(b, JOrderedSetT::kAnyMatch, newIndex);
	assert( found );

	return kJTrue;
}
JIndex
JFSBindingList::AddBinding
	(
	JFSBinding* b
	)
{
	JBoolean found;
	const JIndex index =
		itsBindingList->SearchSorted1(b, JOrderedSetT::kLastMatch, &found);
	if (found)
		{
		JFSBinding* origB = itsBindingList->NthElement(index);
		if (b->IsSystemBinding() && !origB->IsSystemBinding())
			{
			if (!itsOverriddenList->InsertSorted(b, kJFalse))
				{
				delete b;
				return 0;
				}
			}
		else if (!b->IsSystemBinding() && origB->IsSystemBinding() &&
				 itsOverriddenList->InsertSorted(origB, kJFalse))
			{
			itsBindingList->RemoveElement(index);
			}
		else
			{
			itsBindingList->DeleteElement(index);
			}
		}

	JIndex i;
	const JBoolean inserted = itsBindingList->InsertSorted(b, kJFalse, &i);
	assert( inserted );
	return i;
}
void
JXFSBindingManager::Receive
(
    JBroadcaster*	sender,
    const Message&	message
)
{
    if (sender == itsEditDialog && message.Is(JXDialogDirector::kDeactivated))
    {
        itsEditDialog = NULL;
    }

    else if (sender == itsRunFileDialog && message.Is(JXDialogDirector::kDeactivated))
    {
        const JXDialogDirector::Deactivated* info =
            dynamic_cast<const JXDialogDirector::Deactivated*>(&message);
        assert(info != NULL);
        if (info->Successful())
        {
            assert( HasFiles() );

            JFSBinding::CommandType type;
            JBoolean singleFile, saveBinding;
            const JString& cmd = itsRunFileDialog->GetCommand(&type, &singleFile, &saveBinding);

            if (itsIgnoreBindingsFlag && itsRunFileIndex == 1)
            {
                JSize count = itsFileList->GetElementCount();
                for (JIndex i=1; i<=count; i++)
                {
                    JFSBinding* f = itsFileList->NthElement(i);
                    f->SetCommand(cmd, type, singleFile);
                }
            }
            else
            {
                JFSBinding* f = itsFileList->NthElement(itsRunFileIndex);
                f->SetCommand(cmd, type, singleFile);
            }

            if (saveBinding)
            {
                JFSBinding* f = itsFileList->NthElement(itsRunFileIndex);
                SaveBinding(f->GetPattern(), cmd, type, singleFile);
            }

            itsRunFileDialog = NULL;
            ProcessFiles();
        }
        else
        {
            delete itsFileList;
            itsFileList      = NULL;
            itsRunFileDialog = NULL;
        }
    }

    else if (sender == itsRunScriptDialog && message.Is(JXDialogDirector::kDeactivated))
    {
        const JXDialogDirector::Deactivated* info =
            dynamic_cast<const JXDialogDirector::Deactivated*>(&message);
        assert(info != NULL);
        if (info->Successful())
        {
            JFSBinding::CommandType type;
            const JString& cmd = itsRunScriptDialog->GetCommand(&type);
            Exec(itsScriptPath, cmd, type);
        }
        itsRunScriptDialog = NULL;
    }

    else if (sender == itsUpdateBindingListTask && message.Is(JXTimerTask::kTimerWentOff))
    {
        if (itsEditDialog == NULL && itsRunFileDialog == NULL)
        {
            itsBindingList->RevertIfModified();
        }
        else if (itsEditDialog != NULL)
        {
            itsEditDialog->CheckIfNeedRevert();
        }
    }

    else
    {
        JBroadcaster::Receive(sender, message);
    }
}
void
JXFSBindingManager::Exec
(
    const JIndex startIndex,
    const JIndex endIndex
)
{
    // check if same path for all files

    JBoolean samePath = kJTrue;

    JString path, name;
    if (endIndex > startIndex)
    {
        JString p;
        for (JIndex i=startIndex; i<=endIndex; i++)
        {
            const JString& fullName = (itsFileList->NthElement(i))->GetPattern();
            JSplitPathAndName(fullName, &p, &name);
            if (i > startIndex && p != path)
            {
                samePath = kJFalse;
                break;
            }
            path = p;
        }
    }

    // build $q, $u, $qf, $uf

    JString q, u, qf, uf;
    for (JIndex i=startIndex; i<=endIndex; i++)
    {
        const JString& fullName = (itsFileList->NthElement(i))->GetPattern();

        if (samePath)
        {
            JSplitPathAndName(fullName, &path, &name);
        }
        else
        {
            name = fullName;
        }

        q += JPrepArgForExec(name);
        q += " ";

        u += name;
        u += " ";

        qf += JPrepArgForExec(fullName);
        qf += " ";

        uf += fullName;
        uf += " ";
    }

    // run command

    JFSBinding* f = itsFileList->NthElement(startIndex);
    JSplitPathAndName(f->GetPattern(), &path, &name);

    JFSBinding::CommandType type;
    JBoolean singleFile;
    JString cmd = f->GetCommand(&type, &singleFile);

    assert( ( singleFile && startIndex == endIndex) ||
            (!singleFile && startIndex <= endIndex) );

    BuildCommand(&cmd, q, u, qf, uf);
    Exec(path, cmd, type);
}
void
JXFSBindingManager::ProcessFiles()
{
    if (!HasFiles() || itsRunFileDialog != NULL)
    {
        return;
    }

    // check for file with no command

    JSize count = itsFileList->GetElementCount();
    for (JIndex i=1; i<=count; i++)
    {
        JFSBinding* f           = itsFileList->NthElement(i);
        const JString& fileName = f->GetPattern();

        JFSBinding::CommandType type;
        JBoolean singleFile;
        const JString& cmd = f->GetCommand(&type, &singleFile);

        const JFSBinding* b;
        if (!itsIgnoreBindingsFlag &&
                cmd.IsEmpty() && itsBindingList->GetBinding(fileName, &b))
        {
            const JString& cmd = b->GetCommand(&type, &singleFile);
            f->SetCommand(cmd, type, singleFile);
        }
        else if (cmd.IsEmpty())
        {
            assert( itsRunFileDialog == NULL );
            itsRunFileDialog =
                new JXFSRunFileDialog(fileName, JNegate(count > 1 && itsIgnoreBindingsFlag));
            assert( itsRunFileDialog != NULL );
            ListenTo(itsRunFileDialog);
            itsRunFileDialog->BeginDialog();
            itsRunFileIndex = i;
            return;
        }
    }

    // exec one-at-a-time cmds

    (JXGetApplication())->DisplayBusyCursor();

    for (JIndex i=1; i<=count; i++)
    {
        JFSBinding::CommandType t;
        JBoolean singleFile;
        (itsFileList->NthElement(i))->GetCommand(&t, &singleFile);

        if (singleFile)
        {
            Exec(i, i);
            itsFileList->DeleteElement(i);
            count--;
            i--;	// compensate for shift
        }
    }

    // group remaining files and exec

    if (count > 0)
    {
        itsFileList->SetCompareFunction(CompareCommands);
        itsFileList->Sort();

        JIndex startIndex = 1;
        JString cmd;
        JFSBinding::CommandType type = JFSBinding::kRunPlain;

        for (JIndex i=1; i<=count; i++)
        {
            JFSBinding* f           = itsFileList->NthElement(i);
            const JString& fileName = f->GetPattern();

            JFSBinding::CommandType t;
            JBoolean singleFile;
            const JString& c = f->GetCommand(&t, &singleFile);
            assert( !singleFile );

            if (i == startIndex)
            {
                cmd  = c;
                type = t;
            }
            else if (c != cmd || type != t)
            {
                Exec(startIndex, i-1);

                startIndex = i;
                cmd        = c;
                type       = t;
            }
        }

        Exec(startIndex, count);
    }

    delete itsFileList;
    itsFileList = NULL;
}