Progmar Marcin Załęczny

Podpięcie akcji pod przyciski toolbara

Na początek skopiuj do nowego katalogu kod przygotowany w rozdziale 7 ("Piszemy kod przeglądarki internetowej cz.1"). Otwórz w geany pliki MainWindow.h oraz MainWindow.cpp. Do pliku MainWindow.h dodaj funkcję obsługi kliknięcia przycisku tbOpen:


    static void btnGoClicked(GtkWidget* button, MainWindow* window);
    static void tbOpenClicked(GtkWidget* button, MainWindow* window);

protected:
    

Jak zapewne pamiętasz funkcja obsługi kliknięcia przycisku ma postać:


void buttonClicked(GtkWidget* button, gpointer user_data);
    

W naszym przypadku do sygnatury dochodzi słowo kluczowe static oznaczające, że jest to funkcja statyczna klasy. Jako funkcje obsługi zdarzeń mogą być użyte wyłącznie zwykłe funkcje globalne oraz metody statyczne klas.

Okno dialogowe GtkFileChooserDialog

Metoda tbOpenClicked powinna otworzyć okienko dialogowe wyboru pliku i otworzyć wybrany plik html w przeglądarce. Napiszmy teraz w pliku MainWindow.cpp kod tej metody:


void MainWindow::tbOpenClicked(GtkWidget* button, MainWindow* window) {
    GtkWidget *dialog;
    dialog = gtk_file_chooser_dialog_new("Otwórz plik", GTK_WINDOW(window->wnd),
        GTK_FILE_CHOOSER_ACTION_OPEN,
        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
        GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
        NULL);

    GtkFileFilter *fileFilter = gtk_file_filter_new();
    gtk_file_filter_add_pattern(fileFilter, "*.html");
    gtk_file_filter_add_pattern(fileFilter, "*.htm");
    gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), fileFilter);

    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
        gchar *filename;
        filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
        gchar *fileUri = g_strdup_printf("file://%s", filename);
        webkit_web_view_load_uri(window->webView, fileUri);
        g_free(filename);
        g_free(fileUri);
    }

    gtk_widget_destroy (dialog);
}
    

Co się dzieje w tej metodzie? Otóż najpierw tworzone jest okno dialogowe wyboru pliku przy pomocy funkcji gtk_file_chooser_dialog_new. Składnia tej funkcji jest następująca:


GtkWidget* gtk_file_chooser_dialog_new(const gchar *title,
    GtkWindow *parent,
    GtkFileChooserAction action,
    const gchar *first_button_text,
    ...);
    

Jako pierwszy argument przekazujemy tytuł okna, jako drugi - wskaźnik do okna, które będzie rodzicem tworzonego dialogu. Trzeci parametr określa typ okna, w naszym przypadku to GTK_FILE_CHOOSER_ACTION_OPEN - czyli wybór pliku do otwarcia. Na końcu podajemy parami wartości przyciski, które mają się pojawić. Najpierw podajemy tekst przycisku a następnie zwracaną przez niego wartość po nim tekst następnego przycisku i zwracaną przez niego wartość. Jeśli użyjemy tak zwanych przycisków szablonowych (STOCK items), to przyciski będą miały dodatkowo odpowiednią ikonkę. W przykładzie powyżej dodaliśmy przycisk GTK_STOCK_CANCEL zwracający wartość GTK_RESPONSE_CANCEL i przycisk GTK_STOCK_OPEN zwracający GTK_RESPONSE_ACCEPT. Wyszczególnianie przycisków kończymy wartością NULL.

Następnie tworzymy filtr zapewniający, że w okienku dialogowym wyświetlane będą wyłącznie pliki *.html i *.htm:


GtkFileFilter *fileFilter = gtk_file_filter_new();
gtk_file_filter_add_pattern(fileFilter, "*.html");
gtk_file_filter_add_pattern(fileFilter, "*.htm");
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), fileFilter);
    

Nowy filtr tworzymy przy pomocy funkcji gtk_file_filter_new a dozwolone wartości tego filtru dodajemy przy pomocy funkcji gtk_file_filter_add_pattern. Na koniec - przy pomocy funkcji gtk_file_chooser_set_filter ustawiamy ten filtr w oknie dialogowym.

Teraz jesteśmy gotowi do wyświetlenia okna dialogowego. Robimy to przy pomocy funkcji gtk_dialog_run. Wyświetla ona nasz dialog i powróci dopiero po naciśnięciu któregokolwiek przycisku lub krzyżyka zamykającego okno. Jako rezultat zwróci ona wartość odpowiadającą wciśniętemu przyciskowi. Jeśli zwróci ona wartość GTK_RESPONSE_ACCEPT, to ładujemy wybrany plik do przeglądarki:


if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
    gchar *filename;
    filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
    gchar *fileUri = g_strdup_printf("file://%s", filename);
    webkit_web_view_load_uri(window->webView, fileUri);
    g_free(filename);
    g_free(fileUri);
}
    

Wybrany plik pobierzemy przy pomocy funkcji gtk_file_chooser_get_filename. Zwraca ona pełną ścieżkę do wybranego pliku. Musimy jeszcze dokleić na początku nazwy pliku wartość file://, tak żeby mógł on być otworzony przez przeglądarkę. Robimy to przy pomocy funkcji g_strdup_printf, która tworzy nowy łańcuch znaków na podstawie przekazanego szablonu i parametrów. Plik wyświetlamy w przeglądarce przy pomocy funkcji webkit_web_view_load_uri, z którą się już zetknęliśmy w poprzednim rozdziale. Pozostało już tylko zwolnienie pamięci zajmowanej przez zmienne filename i fileUri. O tym, która funkcja tworzy nowy łańcuch znaków, który później musimy zwolnić dowiemy się z dokumentacji zawartej w programie devhelp.

Pozostało już tylko podpięcie stworzonej metody pod sygnał kliknięcia przycisku. W tym celu do konstruktora klasy MainWindow dodaj kod wyszczególniony poniżej grubszą czcionką:


GtkWidget* btnGo = gui->getWidget("btnGo");
g_signal_connect(btnGo, "clicked", G_CALLBACK(MainWindow::btnGoClicked), this);
GtkWidget* tbOpen = gui->getWidget("tbOpen");
g_signal_connect(tbOpen, "clicked", G_CALLBACK(MainWindow::tbOpenClicked), this);

webkit_web_view_load_uri(this->webView, this->homePage);
gtk_widget_grab_focus(GTK_WIDGET(this->webView));
    

Powrót na stronę domową

Stwórzmy teraz funkcję obsługi przycisku Strona domowa. Po jego kliknięciu zostaniemy przeniesieni na stronę zapisaną w atrybucie MainWindow::homePage, czyli "http://www.webkitgtk.org/". Oto nagłówek tej metody w pliku MainWindow.h:


    static void tbOpenClicked(GtkWidget* button, MainWindow* window);
    static void tbHomeClicked(GtkWidget* button, MainWindow* window);

protected:
    

A tu definicja metody tbHomeClicked na końcu pliku MainWindow.cpp:


void MainWindow::tbHomeClicked(GtkWidget* button, MainWindow* window) {
    webkit_web_view_load_uri(window->webView, window->homePage);
    gtk_widget_grab_focus(GTK_WIDGET(window->webView));
}
    

a poniżej podpięcie w konstruktorze tej metody pod sygnał clicked przycisku tbHome:


    GtkWidget* tbOpen = gui->getWidget("tbOpen");
    g_signal_connect(tbOpen, "clicked", G_CALLBACK(MainWindow::tbOpenClicked), this);
    GtkWidget* tbHome = gui->getWidget("tbHome");
    g_signal_connect(tbHome, "clicked", G_CALLBACK(MainWindow::tbHomeClicked), this);

    webkit_web_view_load_uri(this->webView, this->homePage);
    gtk_widget_grab_focus(GTK_WIDGET(this->webView));
    

Zamknięcie programu - emisja sygnału delete-event

Obsłużmy teraz przycisk tbClose. W tym celu otwórz plik MainWindow.h i dodaj nagłówek metody tbCloseClicked:


    static void tbHomeClicked(GtkWidget* button, MainWindow* window);
    static void tbCloseClicked(GtkWidget* button, MainWindow* window);

protected:
    

Następnie na końcu pliku MainWindow.cpp dodaj definicję tej metody:


void MainWindow::tbCloseClicked(GtkWidget* button, MainWindow* window) {
    gboolean retval;
    g_signal_emit_by_name(window->wnd, "delete-event", NULL, &retval);
    if (!retval) {
        gtk_widget_destroy(window->wnd);
    }
}
    

Sygnał emitujemy przy pomocy funkcji g_signal_emit_by_name, która przyjmuje następujące parametry:

  • widgeta, do którego emitujemy sygnał,
  • nazwę emitowanego sygnału,
  • dodatkowy argument user_data dla tego sygnału,
  • adres zmiennej, która otrzyma zwróconą przez funkcję obsługi sygnału wartość.

Jak pamiętamy jeśli funkcja obsługi sygnału delete-event zwróci wartość FALSE, to okno należy zamknąć. Niestety tutaj musimy to zrobić ręcznie gdyż zdarzenie destroy nie zostanie wywołane automatycznie.

Pozostało jeszcze podpiąć funkcję obsługi kliknięcia przycisku. Robimy to w konstruktorze:


g_signal_connect(tbHome, "clicked", G_CALLBACK(MainWindow::tbHomeClicked), this);
GtkWidget* tbClose = gui->getWidget("tbClose");
g_signal_connect(tbClose, "clicked", G_CALLBACK(MainWindow::tbCloseClicked), this);

webkit_web_view_load_uri(this->webView, this->homePage);
    

Wyświetlenie okna dialogowego o programie

Najpierw musimy okno dialogowe zaprojektować. W tym celu otwórz program glade i dodaj okno About Dialog do projektu:

About Dialog in the toolbox    Empty About Dialog

Następnie zapisz projekt pod nazwą about_dialog.ui w katalogu ze źródłami i plikiem main_window.ui. Teraz ustaw właściwości okna dialogowego zgodnie z poniższą tabelą:

Nazwa właściwości Wartość właściwości
Nazwa dlgAbout
Nazwa programu Internet viewer
Wersja programu wersja: 1.00
Prawa autorskie Autor: Marcin Załęczny
Ciąg opisowy Prosta przeglądarka internetowa
Adres strony domowej http://progmar.net.pl
Autorzy Marcin Załęczny
Logo insert-image.png
About Dialog Properties

Jako logo wykorzystałem ikonę systemową /usr/share/icons/gnome/48x48/actions/insert-image.png, którą skopiowałem do katalogu ze źródłami projektu. Nasze okienko dialogowe prezentuje się następująco:

Complete About Dialog

Mamy już gotowe okienko O programie, więc dodajmy w kodzie funkcję obsługi kliknięcia przycisku tbAbout. Zacznijmy od dodania nagłówka funkcji w pliku MainWindow.h:


    static void tbCloseClicked(GtkWidget* button, MainWindow* window);
    static void tbAboutClicked(GtkWidget* button, MainWindow* window);

protected:
    

Teraz w pliku MainWindow.cpp na samym końcu dodajmy definicję tej funkcji:


void MainWindow::tbAboutClicked(GtkWidget* button, MainWindow* window) {
    if (!gui->loadInterface("about_dialog.ui")) {
        return;
    }
    GtkWidget *dialog = gui->getWidget("dlgAbout");
    gtk_dialog_run(GTK_DIALOG(dialog));
    gtk_widget_destroy(dialog);
}
    

I pozostało jeszcze podpięcie funkcji w konstruktorze:


g_signal_connect(tbClose, "clicked", G_CALLBACK(MainWindow::tbCloseClicked), this);
GtkWidget* tbAbout = gui->getWidget("tbAbout");
g_signal_connect(tbAbout, "clicked", G_CALLBACK(MainWindow::tbAboutClicked), this);

webkit_web_view_load_uri(this->webView, this->homePage);
    

Mamy już podpiętą obsługę kliknięć we wszystkie przyciski toolbara, w następnym rozdziale zajmiemy się obsługą funkcji z menu głównego naszego programu.