Blog (76)
Komentarze (5.6k)
Recenzje (0)
@nintyfanlibgreattao: prosty edytor tekstu

libgreattao: prosty edytor tekstu

Właśnie informuję, że wydałem nową wersję libgreattao(wciąż pre‑alfa). Dodałem obsługę nowych tagów do plików opisu klas. Shell od teraz obsługuje zmienne.Jest więcej zmian.

Chciałbym się teraz zająć pisaniem prostej aplikacji - edytora tekstu. Ten edytor tekstu był dostępny jako program demonstracyjny w poprzednim wydaniu, teraz się rozwinął.

Zatem do dzieła! Tak będzie wyglądać ostateczny kod:


#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

void *Window, *dialogWindow;
int edited;
char *backup_file;
char template[] = "/tmp/tao-editor-XXXXXX";
void *save_timer;
void timer_callback(void *data);

void config_callback(void *mesh, void *data)
{
  int seconds, error;
  if ((int) data == 1) {
    seconds = tao_get_integer(dialogWindow, "/category/filesystem/timeout/text", &error);
    if (error || seconds < 1) {
      return;
    }
    tao_remove_timer(save_timer);
    save_timer = tao_add_timer(seconds * 1000, timer_callback, 0);
  }
  else if ((int) data == 2) {
    seconds = tao_get_integer(dialogWindow, "/category/filesystem/timeout/text", &error);
    if (error || seconds < 1) {
      return;
    }
    tao_remove_timer(save_timer);
    save_timer = tao_add_timer(seconds * 1000, timer_callback, 0);
    tao_release_window(dialogWindow);
    dialogWindow = NULL;
  }
  else if ((int) data == 3) {
    tao_release_window(dialogWindow);
    dialogWindow = NULL;
  }
}

void dialog_open_callback(void *mesh, void *data)
{
  int error;
  char *filepath = NULL;
  char *buffer;
  int length;
  int fd;

  if (data == 1) {
    filepath =  tao_get_string(dialogWindow, "/file_name", &error);
  }

  tao_release_window(dialogWindow);
  dialogWindow = NULL;

  if (filepath) {
    
    fd = open(filepath, O_RDONLY);
    if (fd == -1) {
      return;
    }
    length = lseek(fd, 0, SEEK_END);
    lseek(fd, 0, SEEK_SET);
    
    buffer = (char *) malloc(length+1);
    read(fd, buffer, length);
    buffer[length] = 0;
    tao_set_string(Window, "/text", buffer);
    free(buffer);
    close(fd);
  }
}

void timer_callback(void *data)
{
  int fd;
  char *text;
  int error;

  if (!edited) {
    return;
  }

  if (!backup_file) {
    backup_file = mktemp(template);
  }

  text = tao_get_string(Window, "/text", &error);
  if (error) {
    return;
  }
  fd = open(backup_file, O_RDWR | O_CREAT);
  if (fd == -1) {
    return;
  }
  write(fd, text, strlen(text));

  close(fd);

  edited = 0;
}

void dialog_save_callback(void *mesh, void *data)
{
 int error;
 char *filepath = 0;
 char *text;
 int fd;

  if (data == 3) {
    filepath =  tao_get_string(dialogWindow, "/file_name", &error);
  }

  tao_release_window(dialogWindow);
  dialogWindow = NULL;

  text = tao_get_string(Window, "/text", &error);
  if (!text || error || !filepath) {
    return;
  }
  fd = open(filepath, O_RDWR | O_CREAT);
  if (fd == -1) {
    return;
  }
  ftruncate(fd, 0);
  write(fd, text, strlen(text));
  close(fd);

}

void callback(void *mesh, void *data)
{
  if ((int) data == 0) {
    if (dialogWindow) {
      tao_release_window(dialogWindow);
    }
    
    tao_release_window(Window);
    
    tao_close();
  }
  else if ((int) data == 1) {
    tao_set_string(Window, "/text", "");
  }
  else if ((int) data == 2) {
    if (dialogWindow) {
      
      return;
    }
    
    dialogWindow = tao_new_window("/desktop/dialogs/open_file_dialog");
    
    tao_add_handler(dialogWindow, "/actions/ok", &dialog_open_callback, 1);
    tao_add_handler(dialogWindow, "/actions/cancel", &dialog_open_callback, 2);    
  }
  else if ((int) data == 3) {

    if (dialogWindow) {
      
      return;
    }
    dialogWindow = tao_new_window("/desktop/dialogs/save_file_dialog");
    
    tao_add_handler(dialogWindow, "/actions/ok", &dialog_save_callback, 3);
    tao_add_handler(dialogWindow, "/actions/cancel", &dialog_save_callback, 4);    
  }
  else if ((int) data == 4) {
    if (dialogWindow) {
      
      return;
    }
    dialogWindow = tao_new_window("/desktop/dialogs/config_dialog");
    tao_add_handler(dialogWindow, "/category/filesystem");
    tao_set_hint(dialogWindow, "/category/filesystem", HINT_NAME, "Backups");
    tao_set_hint(dialogWindow, "/category/filesystem", HINT_DESCRIPTION, "Control how often backups are made and name of backups");
    tao_add_handler(dialogWindow, "/category/filesystem/timeout/text", 0, 0);
    tao_set_hint(dialogWindow, "/category/filesystem/timeout/text", HINT_NAME, "How often doing backups?");
    tao_set_hint(dialogWindow, "/category/filesystem/timeout/text", HINT_DESCRIPTION, "in seconds");

    tao_add_handler(dialogWindow, "/actions/save", config_callback, 1);
    tao_add_handler(dialogWindow, "/actions/ok", config_callback, 2);
    tao_add_handler(dialogWindow, "/actions/cancel", config_callback, 3);
  }
  else if ((int) data == 5) {
    
      edited = 1;
  }
}

int main(int argc, char *argv[])
{
  tao_initialize("Tao demo", "Super", &argc, argv);
  Window=tao_new_window("/app/text_editor");

  tao_add_handler(Window, "/:abort", &callback, 0);
  tao_add_handler(Window, "/text", NULL, 0);
  tao_add_handler(Window, "/text/changed", callback, 5);

  tao_add_handler(Window, "/actions/file", NULL, 0);
  tao_add_handler(Window, "/actions/app", NULL, 0);

  tao_add_handler(Window, "/actions/file/new", &callback, 1);
  tao_add_handler(Window, "/actions/file/open", &callback, 2);
  tao_add_handler(Window, "/actions/file/save", &callback, 3);
  tao_add_handler(Window, "/actions/app/config", &callback, 4);
  tao_add_handler(Window, "/actions/app/close", &callback, 0);

  tao_change_weight(Window, "/actions/file/save", 500);
  tao_change_weight(Window, "/actions/app/close", 500);

  save_timer = tao_add_timer(1000 * 5, timer_callback, 0);

  tao_handle_events();
};

Od czego zacząć? Najpierw musimy się zaznajomić z klasami okien(by wiedzieć, jak aktywować odpowiednie szablony). Ponieważ nie ma jeszcze dokumentacji, to musimy się posiłkować definicjami klas okien. Jako referencję należy zawsze rozpatrywać wygląd/paczkę mWidgets, jako iż jest tworzona przez zespół(czyli mnie) libgreatao i możę korzystać tylko z podstawowych elementów.

Skorzystamy z czterech klas okien:

  • /desktop/app/text_editor/item] /destkop/dialogs/config_dialog
  • /desktop/dialogs/open_file_dialog
  • /desktop/dialogs/save_file_dialog

Pierwszy zostanie wykorzystany na okno główne, drugi zostanie wykorzystany na okno konfiguracji, a dwa pozostałe na okno otwarcia/zamknięcia pliku. Dwa ostatnie są wbudowane, więc nie znajdziemy ich w instalacji, natomiat można je zastąpić własnymi definicjami przez stworzenie plików pod odpowiednimi ścieżkami.

To, co nas interesuje, to templaty(szablony). To właśnie szablony będziemy tworzyć. Szablony mogą być zagnieżdżone jeden w drugim, więć utworzenie elementu podrzędnego wymaga utworzenia elementu podrzędnego(jeszcze nie jest wspierane automatyczne tworzenie rodzica, by utworzyć dziecko). Dodatkowo szablony mogą być względne lub bezwzględne. Względne nie rozpoczynają się od slasha i wymagają rodzica.

Szablony mogą mieć wagę. Oznacza to, że zostanie utworzony, jeżeli naszej akcji przyporządkujemy odpowiednią wagę. Jest to wykorzystywane w /desktop/dialogs/text_editor -- tam przyciski wyświetlane na górnej belce będą dodane, jeżeli ich akcji przyporządkujemy wagę minimum 500. Za to każda akcja (w sensie zaczynająca się na /actions/) będzie widoczna w menu.

Zaczniemy od inicjalizacji naszej biblioteki. To od tego kroku musi zaczynać swe wykonanie każdy program napisany w libgreattao. Podczas incijalizacji biblioteka poszukuje odpowiedniego backendu i wyglądu(zestawu klas okien). Wygląd ona tak:


tao_initialize("Tao demo", "Super", &argc, argv);

Podajemy tam kolejno: nazwę programu, tekst pomocy, wskaźnik do ilości przekazanych argumentów, wskaźnik na wskaźniki argumentów.

Poniżej tworzymy okno. Jako parametr należy podać ścieżkę do klasy okna. Należy zwrócić uwagę na to, że typ zwracanego wyniku to wskaźnik na cokolwiek. Jest to spodowane tym, że struktury danych w libgreattao mogą się zmienić, jak również tym, żę w trybie powłoki zwracany jest inny wynik niż w trybie UI.

Dalej tworzymy akcje. Chodzi o funcję tao_add_handler. Jej pierwszym parametrem jest okno, drugim ścieżka zdarzenia, trzecim wskaźnik na procedurę do wywołania w przypadku aktywacji zdarzenia, czwartym parametr przekazywany tej procedurze,. Jeże,li nie chcemy dostarczyć żadnej procedury obsługi, a chcemy jedynie, by element był widoczny, to przekazujemy NULL.

Tworzymy następujące akcje:

  • /:abort - wbudowana akcja, chodzi o zamknięcie okna
  • /text - zostanie stworzony element textarea
  • [/text/changed - chcmey otrzymywać powiadomienie, kiedy text zostanie zmieniony
  • /actions/file - menu plik
  • /actions/app - menu applikacji
  • /actions/file/new - kasuje zawartość /text
  • /actions/file/open - otwiera plik
  • /actions/file/save - zapisuje plik
  • /actions/app/config - otwiera okno konfiguracji
  • /actions/app/close - kończy aplikację

Dodatkowo akcji /actions/file/save i /actions/app/close będą specjalne, co jest podkreślone nadaniem im wagi 500. W naszej definicji klasy okna takie akcje będą miały dodatkowy aktywator - w formie przycisku pod menu.

Żeby zablokować tworzenie większej liczby okien dialogowych niż jedno, sprawdzamy wskaźnik na okno dialogowe.

Następnie tworzymy czasomierz, by co pięć sekund była wywoływana określona procedura. Procedura ta będzie sprawdzać czy zawartość widżetu textarea została zmieniona i jeżeli tak, to będzie zapisywać zawartość do pliku. Na końcu wywołujemy pętlę obsługi zdarzeń i oddajemy sterowanie do shell-a lub do UI.

W przypadku okna dialogowego tworzynmy jedną kategorię - filesystem - i jedno pole - timeout typu text. Możęmy dodatkowo określić typ pola poprzez wywołanie tao_data_unsigned_int. Nie robimy jednak tego, gdyż sprawdzamy typ podzcas pobierania danych funkcją tao_get_integer. W wywołaniu tym przekazujemy wskaźnik na liczbę całkowitą o nazwie error. Jeżeli nie wystąpił błąd, to error będzie równy 0.

Należy zwrócić uwagę na to, że po kliknięćiu ok lub zapisz, a dodatkowo przy wprowadzeniu liczby, kasujemy stary timer i tworzymy nowy. Na końcu wstawianie wartości i pobieranie z textarea. Tutaj z pomocą przychodzą wywołania tao_set_string i tao_get_string. Tao_get_string przyjmuje dodatkowo wskaźnik na error. Jeżeli error jest różny od zera, to znaczy, że wystąpił błąd.

W celu sprawdzenia czy użytkownik wybrał plik w dialogu wyboru pliku(obojętnie czy zapisu czy odczytu), posiłkujemy się zdarzeniem /actions/ok. Następnie możemy pobrać ścieżkę wybranego pliku ze ścieżki /file_name.

To już wszystko na dzisiaj.

Wybrane dla Ciebie
Komentarze (1)