Author Topic: External editor for CodeWarrior  (Read 4772 times)

Offline OS923

  • Platinum Member
  • *****
  • Posts: 888
External editor for CodeWarrior
« on: October 21, 2020, 08:11:24 AM »
I'm preparing to write an external editor for CodeWarrior. You may post your feature requests here.

Offline OS923

  • Platinum Member
  • *****
  • Posts: 888
Re: External editor for CodeWarrior
« Reply #1 on: October 30, 2020, 04:31:10 AM »
The AppleScript support is finished.

Offline Protools5LEGuy

  • Global Moderator
  • Platinum Member
  • *****
  • Posts: 2750
Re: External editor for CodeWarrior
« Reply #2 on: October 30, 2020, 08:11:57 AM »
You may post your feature requests here.

XCode wrapper. Converts XCode code in CodeWarrior code thanks to QuartzToQuickDraw converter and other compatibility layers.
Looking for MacOS 9.2.4

Offline OS923

  • Platinum Member
  • *****
  • Posts: 888
Re: External editor for CodeWarrior
« Reply #3 on: November 04, 2020, 09:05:57 AM »
It's meant for OS 9 only.

CodeWarrior keeps the text in memory as one block just like the data fork. This is not performant, although it can scroll 4000 lines per second.

Features that I want:
- max 32767 lines. This corresponds to the max value of the vertical scrollbar. The value of the scrollbar will then be equal to the number of the line on top of the window.
- the last line can be scrolled to the top of the window without having to append empty lines.
- no horizontal scrollbar.
- no C comments.
- automatic indentation like in REALbasic.
- more choice for syntax coloring.
- faster multiple undo.
- faster search and replace with regular expressions (RE or RE2) and undoable.
- plugins (sort, uppercase, etc), in other words: scriptable in C++ instead of AppleScript.
- switch to a graphical mode where your code is drawn as colored blocks that can be moved around.
- correction of expressions, for example (a!=nil) becomes (a), (a && b==1) becomes (a and (b==1)), a+b<<1 becomes a+(b<<1) and (a and b or c and d) becomes ((a and b) or (c and d)).

This looks like much work, but when it's finished I will save much time.

Offline Bolkonskij

  • Gold Member
  • *****
  • Posts: 207
    • Cornica.org
Re: External editor for CodeWarrior
« Reply #4 on: November 08, 2020, 12:31:26 AM »
Absolutely love the project idea, OS923! In regards to features, nothing comes to mind that you had not yet covered. But consider me a definite "customer" for any alpha or beta. :-)
Reel changer over at cornica.org

Offline OS923

  • Platinum Member
  • *****
  • Posts: 888
Re: External editor for CodeWarrior
« Reply #5 on: November 10, 2020, 11:59:20 AM »
I changed PowerPlant's Document application into an external editor. This works for files that are shorter than 32000 characters. It's possible to compile text that hasn't been saved yet.

Offline lartch

  • Valued Member
  • **
  • Posts: 10
  • New Member
Re: External editor for CodeWarrior
« Reply #6 on: November 13, 2020, 02:55:10 PM »
Hi, lartch here. Is the source code available somewhere? Are you looking for help?

Offline OS923

  • Platinum Member
  • *****
  • Posts: 888
Re: External editor for CodeWarrior
« Reply #7 on: November 14, 2020, 07:00:45 AM »
This will be closed source, but there will be an API for making plugins with examples.

Offline OS923

  • Platinum Member
  • *****
  • Posts: 888
Re: External editor for CodeWarrior
« Reply #8 on: December 02, 2020, 12:55:13 PM »
I'm still adapting my framework to work with documents.
What it can do already:
- About,
- Open desk accessory,
- New,
- Close,
- Close all,
- Quit.

I wrote my own headers for Navigation services.

I'm doing things a bit differently:
- I don't have a hierarchy of panes and commanders like in PowerPlant.
- I have a class for every Apple event, every menu and every menu item. A program is derived from these classes.
- You can't close a modified document. You have to choose "Save" or "Discard". I do this to avoid an endless list of save changes dialogs.
- You can't quit unless all documents have been saved or discarded.
- You can also "Detach" a specified document to make it unspecified and close the file. This has the same effect as opening a stationary.
- I would like to open multiple Find/Replace dialogs, save them and alternate between them.
- I would like to save interface parts as snippets and editions where the interface is defined as a document which can be saved, for example a Find/Replace dialog.

Offline OS923

  • Platinum Member
  • *****
  • Posts: 888
Re: External editor for CodeWarrior
« Reply #9 on: December 03, 2020, 02:37:25 AM »
Some ideas:
  • When opening several documents,
    the first comes in front,
    the second behind the first,
    the third behind the second and so on.
    This is faster than when every new document comes in front.
  • Undo and redo are possible after save.

A strange practice in PowerPlant is to write:
Quote
LDocument.cp line 210
delete this;

LDocument.cp line 439
delete this;

LInPlaceEditField.cp line 200
delete this;

LMacTCPTCPEndpoint.cp line 788
delete this;

LModelObject.cp line 148
delete this;

LModelObject.cp line 164
delete this;

LRadioGroup.cp line 146
delete this;

LSharable.cp line 146
delete this;

LThread.cp line 1041
delete this;

LWindow.cp line 1263
delete this;

LWindow.cp line 1278
delete this;

LWindow.cp line 2490
delete this;
I add the items to a list and when I'm done, I delete the items in the list.

Offline OS923

  • Platinum Member
  • *****
  • Posts: 888
Re: External editor for CodeWarrior
« Reply #10 on: December 06, 2020, 02:47:34 AM »
I wrote a library for Navigation services which is easier to use than StandardFile.
It's the simplest solution that I have ever seen.
All references to Apple events have been removed.
This is now part of the OS 9.3 SDK.
Code: [Select]
class SimpleNavigation
{
public:
    // Global functions.
    static Bool Init_class();
    static Bool Exit_class();
    static Bool CheckIsInFront(bool &isInFront);
    static Bool AskDiscardChanges(NavEventUPP eventProc,
                                  bool &ok);
    static Bool AskSaveChanges(NavEventUPP eventProc,
                               NavAskSaveChangesAction action,
                               NavAskSaveChangesResult &reply);
    static Bool ChooseFile(NavEventUPP eventProc,
                           NavTypeListHandle typeList,
                           bool &ok,
                           FSSpec &file);
    static Bool ChooseFiles(NavEventUPP eventProc,
                            NavTypeListHandle typeList,
                            SInt32 maxFiles,
                            bool &ok,
                            SInt32 &cFiles,
                            FSSpec *files);
    static Bool ChooseFolder(NavEventUPP eventProc,
                             bool &ok,
                             FSSpec &folder);
    static Bool ChooseObject(NavEventUPP eventProc,
                             bool &ok,
                             FSSpec &object);
    static Bool ChooseVolume(NavEventUPP eventProc,
                             bool &ok,
                             FSSpec &volume);
    static Bool GetFile(NavEventUPP eventProc,
                        NavTypeListHandle typeList,
                        bool &ok,
                        FSSpec &file);
    static Bool GetFiles(NavEventUPP eventProc,
                         NavTypeListHandle typeList,
                         SInt32 maxFiles,
                         bool &ok,
                         SInt32 &cFiles,
                         FSSpec *files);
    static Bool NewFolder(NavEventUPP eventProc,
                          bool &ok,
                          FSSpec &folder);
    static Bool PutFile(NavEventUPP eventProc,
                        OSType fileType,
                        OSType fileCreator,
                        bool &ok,
                        FSSpec &file);

    // Forbidden.
private:
    SimpleNavigation();
};

Code: [Select]
int main()
{
    ConsoleError::Init_class();

    cout << "Start.";
    cout << endl;

    #ifdef __DEBUG__
        Error::Show("\pDebug version");
    #endif

    if (!Profile())
        {
        Return_0("\pProfile fails");
        }

    cout << "Finish.";
    cout << endl;

    return 0;
}

Bool Profile()
{
    #ifdef __USE_PROFILER__
        ::ProfilerInit(0,
                       4,
                       5000,
                       100);
    #endif

    const Bool ok=SimpleNavigationTest();

    #ifdef __USE_PROFILER__
        ::ProfilerDump("\pProfiler");
        ::ProfilerTerm();
    #endif

    if (!ok)
        {
        Return_false("\pSimpleNavigationTest fails");
        }

    return True;
}

#ifdef __USE_PROFILER__
    #pragma profile on
#endif

pascal void NavEvent(NavEventCallbackMessage const callBackSelector,
                     NavCBRec* const callBackParms,
                     void*)
{
    // Rem: this function is necessary
    // if you want to move and resize your dialogs,
    // and update background windows.

    if (callBackSelector==kNavCBEvent)
        {
        if (!::SIOUXHandleOneEvent(callBackParms->eventData.eventDataParms.event))
            {
            // Rem: the event was handled by SIOUX.
            }
        }
}

Bool SimpleNavigationTest()
{
    if (!SimpleNavigation::Init_class())
        {
        Return_false("\pInit SimpleNavigation class fails");
        }

    NavEventUPP const eventProc=NewNavEventUPP(NavEvent);

    const Bool ok=TryEveryDialog(eventProc);

    DisposeNavEventUPP(eventProc);

    if (!SimpleNavigation::Exit_class())
        {
        Return_false("\pExit SimpleNavigation class fails");
        }

    if (!ok)
        {
        Return_false("\pTry every dialog fails");
        }

    return True;
}

Bool MakeTypeList(NavTypeListHandle &typeList)
{
    const SInt16 typeCount=6;

    Handle h;
    if (!BoolMemory::NewHandle(sizeof(NavTypeList)+(typeCount-1)*sizeof(OSType),
                               h))
        {
        Return_false("\pNewHandle fails");
        }
   
    typeList=reinterpret_cast<NavTypeListHandle>(h);
    (**typeList).componentSignature='BOBO';
    (**typeList).reserved=0;
    (**typeList).osTypeCount=typeCount;
    (**typeList).osType[0]='CWWP'; // Rem: text processing.
    (**typeList).osType[1]='CWGR'; // Rem: drawing.
    (**typeList).osType[2]='CWPT'; // Rem: paint.
    (**typeList).osType[3]='CWSS'; // Rem: spreadsheet.
    (**typeList).osType[4]='CWDB'; // Rem: database.
    (**typeList).osType[5]='CWCM'; // Rem: communications.

    return True;
}

Bool DeleteTypeLijst(NavTypeListHandle &typeList)
{
    Handle const h=reinterpret_cast<Handle>(typeList);
    if (!BoolMemory::DisposeHandle(h))
        {
        Return_false("\pDisposeHandle fails");
        }

    typeList=nil;

    return True;
}

Bool WaitUntilProgramIsInFront()
{
    bool isInFront;
    do
        {
        if (!SimpleNavigation::CheckIsInFront(isInFront))
            {
            Return_false("\pCheckIsInFront fails");
            }

        EventRecord event;
        const bool hasEvent=::WaitNextEvent(everyEvent,
                                            &event,
                                            6,
                                            nil);
        if (hasEvent)
            {
            if (!::SIOUXHandleOneEvent(&event))
                {
                // Rem: the event was handled by SIOUX.
                }
            }
        }
    while (!isInFront);
    return True;
}

Bool TryEveryDialog(NavEventUPP const eventProc)
{
    if (true)
        {
        if (!Test_AskDiscardChanges(eventProc))
            {
            Return_false("\pTest_AskDiscardChanges fails");
            }
        }

    if (true)
        {
        if (!Test_AskSaveChanges(eventProc))
            {
            Return_false("\pTest_AskSaveChanges fails");
            }
        }

    if (true)
        {
        if (!Test_ChooseFile(eventProc))
            {
            Return_false("\pTest_ChooseFile fails");
            }
        }

    if (true)
        {
        if (!Test_ChooseFiles(eventProc))
            {
            Return_false("\pTest_ChooseFiles fails");
            }
        }

    if (true)
        {
        if (!Test_ChooseFolder(eventProc))
            {
            Return_false("\pTest_ChooseFolder fails");
            }
        }

    if (true)
        {
        if (!Test_ChooseObject(eventProc))
            {
            Return_false("\pTest_ChooseObject fails");
            }
        }

    if (true)
        {
        if (!Test_ChooseVolume(eventProc))
            {
            Return_false("\pTest_ChooseVolume fails");
            }
        }

    if (true)
        {
        if (!Test_GetFile(eventProc))
            {
            Return_false("\pTest_GetFile fails");
            }
        }

    if (true)
        {
        if (!Test_GetFiles(eventProc))
            {
            Return_false("\pTest_GetFiles fails");
            }
        }

    if (true)
        {
        if (!Test_NewFolder(eventProc))
            {
            Return_false("\pTest_NewFolder fails");
            }
        }

    if (true)
        {
        if (!Test_PutFile(eventProc))
            {
            Return_false("\pTest_PutFile fails");
            }
        }

    return True;
}

Bool Test_AskDiscardChanges(NavEventUPP const eventProc)
{
    if (!WaitUntilProgramIsInFront())
        {
        Return_false("\pWait until program is in front fails");
        }

    bool ok;
    if (!SimpleNavigation::AskDiscardChanges(eventProc,
                                             ok))
        {
        Return_false("\pAskDiscardChanges fails");
        }

    return True;
}

Bool Test_AskSaveChanges(NavEventUPP const eventProc)
{
    if (!WaitUntilProgramIsInFront())
        {
        Return_false("\pWait until program is in front fails");
        }

    NavAskSaveChangesResult reply;
    if (!SimpleNavigation::AskSaveChanges(eventProc,
                                          kNavSaveChangesOther,
                                          reply))
        {
        Return_false("\pAskSaveChanges fails");
        }

    if (!SimpleNavigation::AskSaveChanges(eventProc,
                                          kNavSaveChangesClosingDocument,
                                          reply))
        {
        Return_false("\pAskSaveChanges fails");
        }

    if (!SimpleNavigation::AskSaveChanges(eventProc,
                                          kNavSaveChangesQuittingApplication,
                                          reply))
        {
        Return_false("\pAskSaveChanges fails");
        }

    return True;
}

Bool Test_ChooseFile(NavEventUPP const eventProc)
{
    NavTypeListHandle typeList;
    if (!MakeTypeList(typeList))
        {
        Return_false("\pMake type list fails");
        }

    const Bool ok=Test_ChooseFile(eventProc,
                                  typeList);

    if (!DeleteTypeLijst(typeList))
        {
        Return_false("\pDelete type list fails");
        }

    if (!ok)
        {
        Return_false("\pTest_ChooseFile fails");
        }

    return True;
}

Bool Test_ChooseFile(NavEventUPP const eventProc,
                     NavTypeListHandle const typeList)
{
    if (!WaitUntilProgramIsInFront())
        {
        Return_false("\pWait until program is in front fails");
        }

    bool ok;
    FSSpec file;
    if (!SimpleNavigation::ChooseFile(eventProc,
                                      typeList,
                                      ok,
                                      file))
        {
        Return_false("\pChooseFile fails");
        }

    return True;
}

Bool Test_ChooseFiles(NavEventUPP const eventProc)
{
    NavTypeListHandle typeList;
    if (!MakeTypeList(typeList))
        {
        Return_false("\pMake type list fails");
        }

    const Bool ok=Test_ChooseFiles(eventProc,
                                   typeList);

    if (!DeleteTypeLijst(typeList))
        {
        Return_false("\pDelete type list fails");
        }

    if (!ok)
        {
        Return_false("\pTest_ChooseFiles fails");
        }

    return True;
}

Bool Test_ChooseFiles(NavEventUPP const eventProc,
                      NavTypeListHandle const typeList)
{
    if (!WaitUntilProgramIsInFront())
        {
        Return_false("\pWait until program is in front fails");
        }

    const SInt32 maxFiles=5;
    bool ok;
    SInt32 cFiles;
    FSSpec files[maxFiles];
    if (!SimpleNavigation::ChooseFiles(eventProc,
                                       typeList,
                                       maxFiles,
                                       ok,
                                       cFiles,
                                       files))
        {
        Return_false("\pChooseFiles fails");
        }

    return True;
}

Bool Test_ChooseFolder(NavEventUPP const eventProc)
{
    if (!WaitUntilProgramIsInFront())
        {
        Return_false("\pWait until program is in front fails");
        }

    bool ok;
    FSSpec folder;
    if (!SimpleNavigation::ChooseFolder(eventProc,
                                        ok,
                                        folder))
        {
        Return_false("\pChooseFolder fails");
        }

    return True;
}

Bool Test_ChooseObject(NavEventUPP const eventProc)
{
    if (!WaitUntilProgramIsInFront())
        {
        Return_false("\pWait until program is in front fails");
        }

    bool ok;
    FSSpec object;
    if (!SimpleNavigation::ChooseObject(eventProc,
                                        ok,
                                        object))
        {
        Return_false("\pChooseObject fails");
        }

    return True;
}

Bool Test_ChooseVolume(NavEventUPP const eventProc)
{
    if (!WaitUntilProgramIsInFront())
        {
        Return_false("\pWait until program is in front fails");
        }

    bool ok;
    FSSpec volume;
    if (!SimpleNavigation::ChooseVolume(eventProc,
                                        ok,
                                        volume))
        {
        Return_false("\pChooseVolume fails");
        }

    return True;
}

Bool Test_GetFile(NavEventUPP const eventProc)
{
    NavTypeListHandle typeList;
    if (!MakeTypeList(typeList))
        {
        Return_false("\pMake type list fails");
        }

    const Bool ok=Test_GetFile(eventProc,
                               typeList);

    if (!DeleteTypeLijst(typeList))
        {
        Return_false("\pDelete type list fails");
        }

    if (!ok)
        {
        Return_false("\pTest_GetFile fails");
        }

    return True;
}

Bool Test_GetFile(NavEventUPP const eventProc,
                  NavTypeListHandle const typeList)
{
    if (!WaitUntilProgramIsInFront())
        {
        Return_false("\pWait until program is in front fails");
        }

    bool ok;
    FSSpec file;
    if (!SimpleNavigation::GetFile(eventProc,
                                   typeList,
                                   ok,
                                   file))
        {
        Return_false("\pGetFile fails");
        }

    return True;
}

Bool Test_GetFiles(NavEventUPP const eventProc)
{
    NavTypeListHandle typeList;
    if (!MakeTypeList(typeList))
        {
        Return_false("\pMake type list fails");
        }

    const Bool ok=Test_GetFiles(eventProc,
                                typeList);

    if (!DeleteTypeLijst(typeList))
        {
        Return_false("\pDelete type list fails");
        }

    if (!ok)
        {
        Return_false("\pTest_GetFiles fails");
        }

    return True;
}

Bool Test_GetFiles(NavEventUPP const eventProc,
                   NavTypeListHandle const typeList)
{
    if (!WaitUntilProgramIsInFront())
        {
        Return_false("\pWait until program is in front fails");
        }

    const SInt32 maxFiles=5;
    bool ok;
    SInt32 cFiles;
    FSSpec files[maxFiles];
    if (!SimpleNavigation::GetFiles(eventProc,
                                    typeList,
                                    maxFiles,
                                    ok,
                                    cFiles,
                                    files))
        {
        Return_false("\pGetFiles fails");
        }

    return True;
}

Bool Test_NewFolder(NavEventUPP const eventProc)
{
    if (!WaitUntilProgramIsInFront())
        {
        Return_false("\pWait until program is in front fails");
        }

    bool ok;
    FSSpec folder;
    if (!SimpleNavigation::NewFolder(eventProc,
                                     ok,
                                     folder))
        {
        Return_false("\pNewFolder fails");
        }

    return True;
}

Bool Test_PutFile(NavEventUPP const eventProc)
{
    if (!WaitUntilProgramIsInFront())
        {
        Return_false("\pWait until program is in front fails");
        }

    bool ok;
    FSSpec file;
    if (!SimpleNavigation::PutFile(eventProc,
                                   'PICT',
                                   'ogle',
                                   ok,
                                   file))
        {
        Return_false("\pPutFile fails");
        }

    return True;
}

#ifdef __USE_PROFILER__
    #pragma profile off
#endif

Offline OS923

  • Platinum Member
  • *****
  • Posts: 888
Re: External editor for CodeWarrior
« Reply #11 on: December 08, 2020, 11:03:54 AM »
It was originally written in Dutch, and I forgot to replace Lijst with List in some places.
Lijst is Dutch for list, with ij pronounced as e in beta.