Dynamisk Meny

Jeg har valgt å generere alle menyene ved runtime, da har jeg full kontroll. Tidligere fiklet jeg litt med C++Builder’s egen internasjonalisering, men den slo ikke helt an hos meg. Jeg bruker i stedet en variant av noe jeg har laget og brukt tidligere.

Der ligger alle menyteksten i en egen fil, og har du behov for oversettelse til et annet språk, sender du bare denne filen til en translatør.

Slik systemet er her nå, lastes det en DLL og ferdig med det, det er greit nok, men det skulle ikke være alt for vanskelig å skrive om dette til et antall DLL-er og angi f.eks. i en ini-fil hvilken som skal lastes.

Systemet er som følger:

Det hele starter i OnFormCreate:

void __fastcall TForm1::OnFormCreate(TObject *Sender)
{
   ---
   ---
   hLibHandle = LoadLibrary( L"DynDllProject.dll" ); // Menu strings

   if( hLibHandle )
	{

	iret = CreateDynMenu( hLibHandle );
        ---
        ---

Ikke noe hokus-pokus i denne funksjonen altså. Alle menyfunksjonene er lagt i «menu1.cpp», du vil finne den kildefilen i prosjektlisten til høyre i RAD Studio.

Hovedfunksjonen, «CreateDynMenu», burde nok kanskje vært skrevet om litt, ingen vanskelighet det heller, men jeg bruker fortsatt gamle tin som jeg vet funker og for å slippe og tenke for mye 🙂 ( Jeg har utelatt detaljer vedr. testing for oversiktens skyld)

int  __fastcall TForm1::CreateDynMenu(HINSTANCE hLibHandle)
{
         ---	
         ---
	CreateFileMenu( hLibHandle );
	CreateEditMenu( hLibHandle );
	CreateSearchMenu( hLibHandle );
	CreateImageMenu( hLibHandle );
         ---
         ---
         ---

Vi kan da gå videre til funksjonene for hver enkelt meny. De er i prinsippet helt like, omtrent som følgende fra File-menyen:

//---------------------------------------------------------------------------
// ------------------------ CreateFileMenu ----------------------------------
//---------------------------------------------------------------------------

int  __fastcall TForm1::CreateFileMenu(HINSTANCE hLibHandle)
{
	TMenuItem *ParentItem;
	TMenuItem *ParentItem2;
	TMenuItem *ChildItem;

	if( hLibHandle != 0)
	{
		charReturnFunc myFunc;

		// GetProcAddress returns a pointer to the loaded method
		myFunc = (charReturnFunc)GetProcAddress(hLibHandle, "GetFileMenuString");

		if( myFunc != 0)
		{
			ParentItem = new TMenuItem(MenuBar1);
			ParentItem->Text = myFunc(0, 256);          // 0 = "File"
			ParentItem->Name = ParentItem->Text;
			MenuBar1->AddObject(ParentItem );

			ChildItem = new TMenuItem(ParentItem);
			ChildItem->Text = myFunc(1, 256);           // 1 = "New"
			ChildItem->Name = ChildItem->Text;
			ChildItem->OnClick = menu_FILE_NEW_Click;
			ParentItem->AddObject(ChildItem);

Først må du hente opp adressen til funksjonen (det er standard bruk av en DLL) og selve menyteksten hentes opp med «ParentItem->Text = myFunc(0, 256)» hvor første parameter er nummeret til tekststringen og det andre tallet er en maks-verdi.

Komplisert? egentlig ikke, men vi er ikke ferdig ennu. Vi skal videre se på de enkelte menyfunksjoner i DLL-en her vist ved parameter 2 i «myfunc», nemlig «GetFileMenuString».

///////////////////////////////////////////////////////////////////////
//
//   wchar_t* GetFileMenuString(UINT uID, int maxlen)
//
//
wchar_t* GetFileMenuString(UINT uID, int maxlen)
{

	switch (uID) {
		case 0:
			return menu_FILE.c_str();                   // index    0
		case 1:
			return menu_FILE_NEW.c_str();               //  1
		case 2:
			return menu_FILE_OPEN.c_str();              //  2
		case 3:
			return menu_FILE_OPEN_RECENT.c_str();       //  3

OK, nå gjenstår det vel bare selve tekststringene som alle ligger i samme fil:

///////////////////////////////////////////////////////////////////////////////
//
// Lots and lots of string defs
//
// FireMonkey does not like resourcefiles, so
// this is my first solution to that problem.
//
// $Rev: 19001 $
//
// Copyright(c) 2019- Gamlisen Norway
//              All rights reserved
//
// ------------------------------------------------------------------
//  File menu textstrings
//
const String menu_FILE = _T("File");                    	// index 	0
const String menu_FILE_NEW = _T("New");                 	//          1
const String menu_FILE_OPEN = _T("Open");               	//          2
const String menu_FILE_OPEN_RECENT = _T("OpenRecent"); 		//          3
const String menu_FILE_CLOSE = _T("CloseFile");        		//          4
const String menu_FILE_CLOSE_ALL = _T("CloseAll");     		//          5
const String menu_FILE_SAVE = _T("SaveFile");          		//          6
const String menu_FILE_SAVE_AS = _T("SaveFileAs");     		//          7
const String menu_FILE_SAVE_PROJECT = _T("SaveProject"); 	//        	8
const String menu_FILE_SAVE_PROJECT_AS = _T("SaveProjectAs"); //  		9

Nå er vi ferdig med alle de enkelte delene som inngår i min form for dynamisk meny.

Komplisert?

Egentlig ikke når du først har forstått systemet 🙂 Samtlige funksjoner blir forklart i detalj på de enkelte sidene.

FotoBox1 Layout

Nå får jeg muligens et lite problem, vil noen si. Det som står her i blogg-strømmen er jo omtrent det samme som finnes rundt omkring på sidene. Det får så være, men det som står her er alt mulig annet rusk og rask også, som f.eks. WordPress-tester (plugins). Jeg kommer sannsynligvis til å legge inn ideer og løsninger for bl.a Lightwave, men her i bloggstrømmen kommer alt helt ustrukturert etter hvert som jeg arbeider meg fremover.

Alt er skrevet i C++ og jeg bruker C++Builder fra RAD Studio (Embarcadero). Layout ved designtidspunkt ser slik ut:

Fotobox1 Layout

Jeg legger ut en TabControl, men foreløpig ingen Tab Items her, de lages ved runtime. Det har jeg gjort ved en ny klasse som er arvet fra TTabItem (jeg skal vise koden om litt).

Årsaken til dette er at den listboksen som vil forekomme på høyre side i programmet skal vise åpne objekter i hver enkelt tab. Jeg prøvde først å legge denne listboksen i hovedklassen, TForm1, men det ble litt komplisert ved at listboksen måtte cleares hver han man gikk inn i en ny tab med pekere frem og tilbake mellom TForm1 og dokumentklassen.

Bildet nedenfor viser hva jeg mener. Dette bildet er fra runtime hvor jeg har åpnet et bilde (av Gamlisen himself 🙂 ). På høyre side ser du her en listbox hvor navnet på åpne bilder settes inn samt visse taster for å skjule bildene, låse dem, osv. finnes.

Det er funksjoner som er tenkt implementert i neste versjon.

Alt dette lages ved File Open:

void __fastcall TForm1::menu_FILE_OPEN_Click(TObject *Sender)
{
	DocPage *NewItem;

	//Show Image files only
	OpenDialog1->Filter = TBitmapCodecManager::GetFilterString();

	if (OpenDialog1->Execute()) {

	try {

		// Add another Tab Item
		NewItem = new DocPage(TabControl1);
		NewItem->Parent = TabControl1;
		NewItem->Text = OpenDialog1->FileName;


		int maxTabs = TabControl1->TabCount;
		TabControl1->TabIndex = maxTabs-1;

		// Right frame containing Layer list, etc.
		NewItem->myFrame = new TFrame2(NewItem);
		NewItem->myFrame->Parent = NewItem;
		NewItem->myFrame->Width = 276;
		NewItem->myFrame->Align = TAlignLayout::Right;


		// 1. TFramedScrollBox
		NewItem->FramedScrollBox1 = new TFramedScrollBox(NewItem);
		NewItem->FramedScrollBox1->Parent = NewItem;
		NewItem->FramedScrollBox1->Align = TAlignLayout::Client;

		// 2. Layout1
		NewItem->Layout1 = new TLayout(NewItem->FramedScrollBox1);
		NewItem->Layout1->Parent = NewItem->FramedScrollBox1;
		NewItem->Layout1->Align = TAlignLayout::Center;

		// 3. Image1
		NewItem->Image1 = new TImage(NewItem->Layout1);
		NewItem->Image1->Parent = NewItem->Layout1;
		NewItem->Image1->Align =  TAlignLayout::Client;

		// Load From File into Image1
		NewItem->Image1->Bitmap->LoadFromFile(OpenDialog1->FileName);

		// Height and width
		NewItem->Layout1->Height = NewItem->Image1->Bitmap->Height;
		NewItem->Layout1->Width = NewItem->Image1->Bitmap->Width;


		// Add to Layer list
		TLayerItem *myLayerItem = new(TLayerItem);

		myLayerItem->FileName = new String (NewItem->Text);

		// Strip off short filename

		String Delimiters = _T("/\\");
		int ix = myLayerItem->FileName->LastDelimiter( Delimiters);
		ix += 1;

		// cheating a little, saying 30 characters as max length :-)
		myLayerItem->ShortName = new String(myLayerItem->FileName->SubString(ix, 30));

		myLayerItem->LayerName = myLayerItem->ShortName;
		myLayerItem->ElementType = File;
		myLayerItem->LayerFilter = 0;
		myLayerItem->Enabled = True;
		myLayerItem->LayerBitmap =  NewItem->Image1->Bitmap;

		NewItem->LayerList.push_back(myLayerItem);
		NewItem->myFrame->LayerListBox1->Items->Add(*myLayerItem->ShortName);
		NewItem->myFrame->TabControl2->ActiveTab = NewItem->myFrame->Layers;
		NewItem->myFrame->TabControl2->TabIndex = 0;
		//NewItem->myFrame->TabControl2->Tabs[2]->Enabled = False;

		//Enable Menu Items after Open File
		int iret = DisableEnableMenuItems(TRUE);

		}
	catch (...) {
		 ShowMessage(" Error from File Open");
		}

	}
}

Slik det er nå i denne versjonen, vil det lages en ny tab-side hver gang man åpner et bilde. Dette er selvfølgelig ikke godt nok i neste versjon. Da skal det være muligheter for flere åpne bilder i hver tab og kunne flytte disse frem og tilbake på siden f.eks.

Plugin for source code

Tester en ny plugin for å vise kildekode. Det er litt knot å få det til i ren WordPress. Denne plugin ser grei ut, uten fargesettinger, men ellers helt grei. Navnet er Code Snippets, merkelig nok 🙂

Koden nedenfor er funksjonen OnFormCreate, jeg tester lasting av diverse stiler og et stykke lenger ned lastes en DLL , DynDllProject.dll. Der ligger alle menyene og helt nederst er det klargjort for en eventuell MAC versjon.

Jeg kunne muligens hatt en try-catch, men pytt, pytt. Jeg tester på lasting av dll-en likevel 🙂

void __fastcall TForm1::OnFormCreate(TObject *Sender)
{
	//TStyleManager::SetStyleFromFile(L"Transparent.style"); 	//  2
	//TStyleManager::SetStyleFromFile(L"MetropolisUIBlack.style");
	//TStyleManager::SetStyleFromFile(L"GoldenGraphite.style");
	//TStyleManager::SetStyleFromFile(L"EmeraldDark.Win.style");
	//TStyleManager::SetStyleFromFile(L"Diamond.Win.style");
	TStyleManager::SetStyleFromFile(L"Dark.style");         	//  1
	//TStyleManager::SetStyleFromFile(L"CalypsoSE_Win.style");    //  3
	//TStyleManager::SetStyleFromFile(L"Calypso_Win.style");
	//TStyleManager::SetStyleFromFile(L"Blend.style");            //  4
	//TStyleManager::SetStyleFromFile(L"AquaGraphite.style");     //  5
	//TStyleManager::SetStyleFromFile(L"Amakrits.style");         //  6
	//TStyleManager::SetStyleFromFile(L"gamlisensteststyle.style");

	//OptionPanel = 0;
	hLibHandle = LoadLibrary( L"DynDllProject.dll" ); // Menu strings
	pmySelection = 0;

	int iret = 0;

	if( hLibHandle )
	{

	iret = CreateDynMenu( hLibHandle );

// ***                                                  ***
// *** Have to do something with the MAC menu later     ***
// ***                                                  ***
#ifdef macos

	//winabout.Visible = False;
	//winline.Visible = False;
	//winexit.Visible = False;
	//macquit.ShortCut = scCommand or Ord('Q');

#else

	//MacAbout.Visible = False;
	//macline.Visible = False;
	//macquit.Visible = False;

#endif

	}

	ZoomTrackBar->Value = 1;
}

Gamlisens Fotobox

Planen var først å lage et program for enkel bilderedigering, for så ved neste utgave å legge til et dokumentformat, dvs. tilsvarende et skrivebord, et A4 ark (dokument redigering).

Status Jan. 2020:
Fotobox 1 kan anses ferdig med bl.a. copy/paste, crop og filter funksjoner.

Fotobox 2 ikke påbegynt p.g.a misnøye med Firemonkeys grafikkbibliotek. Dette gjør om alle formater til 32 bits RGB internt. Det betyr at om du åpner ett eller annet bildeformat og så lagrer det tilbake, vil det lagres som 32 bits RGB uansett hva det var på forhånd.

Jeg begynner likevel å legge ut eksempler fra kodingen, så får tid og humør bestemme den videre utvikling.

Gråtoner og andre toner

Hvorfor «ødelegger» Firemonkey bildene? Hæææ? Ødelegger??? Yess Sir, de gjør om alle bildeformater internt til RGB!

Jeg tenkte ikke over det før jeg ønsket å få bildeinfo fra biblioteket. Uansett hvilket bilde jeg lastet opp («åpnet»), fikk jeg til svar at det var 32 bit RGB! Det betyr 3×8 bit + alpha.

Etter et par dager med hardkjøring av Google, var svaret gitt. Det bare var sånn!

Hva betydde det for meg? OK, Hvordan lages gråtoner f.eks. på skjermen? Jadajada, skjermen er RGB den! Oissann! Hva med printeren da? Den bruker CMYK, helsort farge og rasterteknikk, min venn! Gråtoner finnes ikke i vår daglige verden!

Finnes det egentlig et sort-hvitt kamera i dag? Jeg er ikke fotograf, så jeg vet egentlig ikke. Jeg tar sjansen og sier nei. Du må antagelig tilbake til de gamle analoge kameraene, med film. Det finnes digitale kamera som tar B&W 🙂

Det finnes en rekke andre spesialformater, jeg er klar over det, men til daglig bruk, ordne og fikse litt på bilder fra mobilen, så er Firemonkey grafikkbibliotek fullt ut brukbart, terningkast 6.

Til profesjonelt bruk, er det ubrukelig.

Et gråtone-bilde fra mitt program:

Gamlisen

RAD Studio

Jeg har lagt inn følgende nytt i menyen (om RAD Studio):

Den leveres med to programmerings språk: Delphi (Pascal) og C++ Builder, og det er C++ jeg programmerer i. Pascal? Som en hardcore C/C++ programmerer «hater» jeg Pascal 🙂 Nå trår jeg antagelig en del på tærne, men Pascal ses på som et skolespråk, C++ er for profene 🙂

Du så jeg avsluttet med et smilefjes, ikke sant?

Skal du skaffe deg en betalt versjon, koster den skjorta og mer til. Heldigvis finnes det en «Community» versjon, og den er gratis. Du må imidlertid velge ett av språkene, C++ eller Delphi (Pascal). Delphi er Borlands object versjon av Pascal. Da må du finne deg i at alt nytt kommer først til Delphi. Delphi er Borland guttas «førstefødte» baby, og slik vil det alltid være.

Som et lite eksempel: Google forlanger nå 64-bits kompilerte programmer. Det har du for Delphi, men ikke (foreløpig?) for C++, kun 32 bits. Noe av det samme har du for linux. Du får Delphi for linux, men ikke C++.

Jeg avslutter den negative vinklingen med: Grafikkbiblioteket for multi-device løsningen, Firemonkey, jobber kun med 32 bits RGB. Kan du leve med at Firemonkey gjør om alle formater til 32-bits RGB internt, er RAD Studio et fantastisk verktøy, etter min mening.

 32-bits RGB problemet gjelder her i Januar 2020. 

Bildeformater

Jeg har nå fått til bruken av de fleste av filterene som følger med RAD Studio. Jeg skal vise koden for noen av eksemplene, men først:
Vær klar over at RAD Sdudio, Firemonkey versjonen, gjør om alle grafiske bilder til 32 Bit RGB fargecode. Dvs. 3 x 8 Bit pluss Alpha. Kan du leve med det, så er RAD Studio en fantastisk løsning.

Du trenger kanskje ikke andre løsninger om du skal lage et program for å ta vare på mobil-bildene dine. En profesjonell utgave av det samme programmet er imidlertid helt ubrukelig fordi den gjør om alle formater til 32 Bit RGB.

Internasjonalisering og stilendring

Neste trinn var å få til en praktisk løsning på internasjonalisering og endring av stil («Style») og farger ved runtime. Hva med en PhotoShop Clone? Hehe, litt over hva jeg kan få til, antagelig, men et skall med tomme menyer er jo mulig 🙂

Det tok forøvrig over én måned bare å taste inn all menyteksten. All teksten ligger nå i string-lister som kan deles ut til en oversetter. Den oversatte teksten legges inn i en DLL som kompileres og linkes.

Resultatet ble ikke så verst, om jeg skal si det selv ut fra bildene nedenfor 🙂

Det siste bildet viser dessuten noen fotofiltre vi får med i C++ Builder 🙂

C++ Builder Intro

Har begynt å legge inn litt brukervegledning for C++ Builder, foreløpig kun på laveste nivå. Jeg kommer ikke til å lage nye innlegg for hver enkel side.

Igjen: Dette er ikke tenkt som lærebok i programmering med C++ Builder, det er mer en huskeliste for meg slik at jeg vet hvor jeg finner ting og tang.

Utvidelse av menyer

Jeg fortsetter med litt mer tekst og reorganisering av menyen. Lagt inn et Første Inntrykk som hovedstopp i menyen.

Førsteinntrykket er vel da omtrent ferdig.