Jacek Matulewski
Projektowanie aplikacji środowiska graficznego przenaszalnych między platformami Windows i Linux


  1. Źródła - projekty korzystające z biblioteki CLX (Kylix 3 w C++):
    1. dynamiczne tworzenie obiektów (opis: RAD1.II.10)
    2. kolory (opis: RAD1.IV.1)
    3. graficzny dywan (opis: RAD1.IV.3)
    4. komponent TLink (opis: RAD1.VI.3)
    5. funkcje obsługi plików (wynajdywanie plików, operacje na plikach i katalogach, funkcje otaczające do funkcji CLX z obsługą błędów)
    6. XEdit
    7. XCommander
  2. Gotowe programy
    1. XEdit/XView
    2. XCommander (w tym XEdit)
  3. Funkcje zastępujące często wykorzystywane funkcje WinAPI:
    1. RGB()
    2. CopyFile()
  4. Funkcje służące do operacji na plikach
    1. Wyszukiwanie wszystkich plików w bieżącym katalogu i umieszczanie ich w liście typu TList
    2. Funkcja IsDirectory() sprawdzająca, czy podana ścieżka to katalog
  5. Funkcja ProgramExecute() pozwalająca na uruchamianie procesów pod Linuxem
  6. Kilka uwag nt. uruchamiania programów poza środowiskiem Kylixa
    1. Uruchamianie w systemie z zainstalowanym Kylixem
    2. Uruchamianie w systemie bez zainstalowanego Kylixa
    3. Biblioteki Qt
  7. Problemy z instalcją Kylix 3 Open Edition pod RedHat 8 i RedHat 9


Funkcja RGB()

#ifndef _RGB
#define _RGB

#include 
int RGB(int r,int g,int b)
	{
	return r+256*g+Power(256,2)*b;
	}

#endif 

Funkcja CopyFile()
#include <System.hpp>
#include <SysUtils.hpp>
#include <Classes.hpp>

bool __fastcall CopyFile(AnsiString source_file,AnsiString target_file,bool nadpisywac)
        {
        if (FileExists(target_file))
                if (nadpisywac)
                        DeleteFile(target_file);
                        else
                        return false;

        TStream* Source=new TFileStream(source_file,fmOpenRead);
        try
                {
                TStream* Target=new TFileStream(target_file,fmCreate);
                        try
                                {
                                Target->CopyFrom(Source,Source->Size);
                                }
                        __finally
                                {
                                delete Target; //usuniecie obiektu strumienia (plik zostaje)
                                }
                }
        __finally
                {
                delete Source; //usuniecie obiektu strumienia (plik zostaje)
                }
        return true;
        } 

Funkcja wyszukująca pliki z podanego katalogu + struktura przechowywanych danych + funkcja wykorzystywana przy sortowaniu
//struktura uproszczona wzgledem TSearchRec
struct opis_pliku
        {
        AnsiString nazwa_pliku;
        bool czy_katalog;
        int poziom;
        };

//funkcja sluzaca do porownywania przy sortowaniu elementow listy
int __fastcall porownaj_opisy_plikow(void* rekord1,void* rekord2)
        {
        return ((opis_pliku*)rekord1)->poziom-((opis_pliku*)rekord2)->poziom;
        }

//---------------------------------------------------------------------------

void pliki_w_katalogu(AnsiString katalog_maska,TList* lista,bool rekurencyjnie)
{
AnsiString katalog=ExtractFileDir(katalog_maska);
//katalog_maska musi miec maske np. *
//zob. example do FindFirst
int Atrybuty=faReadOnly | faHidden | faSysFile | faVolumeID | faDirectory | faArchive | faAnyFile;
TSearchRec sr;

static int poziom=0;

if (FindFirst(katalog_maska,Atrybuty,sr)==0) //zajmuje pamiec
        {
        do
                {
                if (sr.Name!="." && sr.Name!="..")
                        {
                        opis_pliku* rekord=new opis_pliku;
                        //musze tworzyc dynamicznie, bo inaczej elementy listy beda zapominane po wyjsciu z biezacego zakresu
                        rekord->nazwa_pliku=katalog+PathDelim+sr.Name;
                        rekord->czy_katalog=(sr.Attr & faDirectory)==faDirectory;
                        rekord->poziom=poziom;
                        lista->Add(rekord);
                        if (rekord->czy_katalog && rekurencyjnie)
                                {
                                poziom++;
                                pliki_w_katalogu(rekord->nazwa_pliku+PathDelim+'*',lista,rekurencyjnie);
                                poziom--;
                                }
                        }
                }
        while (FindNext(sr)==0);
        FindClose(sr); //zwalnia pamiec
        }
} 

Funkcja sprawdzająca, czy podana ścieżka jest katalogiem
//sprawdzenie czy podana sciezka to katalog
bool IsDirectory(AnsiString katalog)
        {
        bool wynik=false;
        int Atrybuty=faReadOnly | faHidden | faSysFile | faVolumeID | faDirectory | faArchive | faAnyFile;
        TSearchRec sr;

        if (FindFirst(katalog,Atrybuty,sr)==0)
                {
                wynik= ((sr.Attr & faDirectory)==faDirectory);
                }

        FindClose(sr); //zwalnia pamiec
        return wynik;
        }

Funkcja służąca do uruchomienia innego programu o podanej ścieżce i argumentach z poziomu naszego programu
#include <stdio.h>
#include <fcntl.h>

void __fastcall ProgramExecute(AnsiString ansi_path,AnsiString ansi_arg1,AnsiString ansi_arg2)
{
const bool silence=true;
if (!silence) ShowMessage((AnsiString)"Proces nadrzedny PID="+getpid());

int arg_no=2;
if (ansi_arg2.IsEmpty() || ansi_arg2==NULL) arg_no=1;
if (ansi_arg1.IsEmpty() || ansi_arg1==NULL) arg_no=0;
char* path=ansi_path.c_str();
char* arg0="";
char* arg1=ansi_arg1.c_str();
char* arg2=ansi_arg2.c_str();

int open_max;

pid_t pid=fork();
//if (pid==0) execlp("xterm","-n XCommander-XTerm",NULL,NULL);return;
//if (pid==0) execlp("XEdit","","-v","FindAllFiles.~cpp",NULL);return;

switch(pid)
        {
        case -1:
                //Wykonywane w procesie nadrzednym, proces potomny nie powstaje
                if (!silence) ShowMessage("Can't create child process!"); break;

        case 0:
                //Wykonywane w procesie potomnym jezeli fork sie uda
                if (!silence) ShowMessage((AnsiString)"Proces potomny PID="+getpid());
                //Zamykanie otwartych plikow w procesie potomnym (i tak zostanie zastapiony)
                open_max=sysconf(_SC_OPEN_MAX);
                for(int i=(int)stderr+1;i<open_max;i++) fcntl(i,F_SETFD,FD_CLOEXEC);
                switch(arg_no)
                        {
                        case 0: execlp(path,NULL); break;
                        case 1: execlp(path,arg0,arg1,NULL); break;
                        case 2: execlp(path,arg0,arg1,arg2,NULL); break;
                        }
                break;

        default:
                //Wykonywane w procesie nadrzednym
                if (!silence) ShowMessage((AnsiString)"Proces nadrzedny stworzyl proces PID="+pid);
        }
} 

Kilka uwag nt. uruchamiania programów poza środowiskiem Kylixa

Aby uruchomić program napisany w Kylix 3 w dowolnym środowisku XWin w systemie Linux należy udostępnić programowi następujący zbiór bibliotek ładowanych dynamicznie:

libborqt-6.9.0-qt2.3.so
libborqt-6.9-qt2.3.so -> libborqt-6.9.0-qt2.3.so

libqtintf-6.9.0-qt2.3.so
libqtintf-6.9-qt2.3.so -> libqtintf-6.9.0-qt2.3.so

libqt.so.2.3.0
libqt.so.2 -> libqt.so.2.3.0

Jeżeli w systemie został znaleziony Kylix 3 znajdują się domyślnie w katalogu /usr/local/kylix3/bin. Jeżeli nie, muszą być dostarczone razem z aplikacją (zob. link w spisie treści). Przed uruchomieniem programu (poza środowiskiem Kylix) należy wskazać położenie tych bibliotek za pomocą zmiennej środowiskowej LD_LIBRARY_PATH. Można to zrobić pisząc odpowiedni skrypt, np.:

#!/bin/bash
declare -x LD_LIBRARY_PATH=/usr/local/kylix3/bin:$LD_LIBRARY_PATH
XCommander &
Gdy chcemy wykorzystać środowisko tcsh skrypt wyglądałby następująco:

#!/bin/tcsh
setenv LD_LIBRARY_PATH /usr/local/kylix3/bin
XCommander 
Zmienną można również ustawić na stałe (w pliku konfigurującym środowisko) i wówczas programy mogą być wywoływane bezpośrednio.

Problemy z instalcją Kylix 3 Open Edition pod RedHat 8 i RedHat 9

W RedHat 8 i 9 po instalcji Kylix 3 należy w ścieżce przeszukiwania projektów C++ ustawić katalog /bin/include na pierwszym miejscu. W tym celu należy:
1) uruchomić Kylix 3 dla C++,
2) z menu Project, Options... wybrać polecenie Project Options for Default
3) przejść do zakładki Directories/Conditionals,
4) w polu Include path należy przesunąć katalog /usr/include na pierwszą pozycję

W RedHat 9: jeżeli pojawia się błąd "unresolved external" należy zainstalować compact-glibc-6.2-2.1.43 z RedHat 7

  Do początku stony
  Powrót