|
||||||||||||||||||||||||||||||||||||||||||
Ahmet Zeki EYMÜR's Hardware and Software Products |
||||||||||||||||||||||||||||||||||||||||||
|
Products:
AZEskop Sound Card Oscilloscope
|
C64TPC (Connect C64 to PC)
|
Programmable USB Footswitch
|
TimeSticker Software
|
Articles(Turkish): GTK+'da Fare Olaylarının Düşük Seviyeli Yönetimi | Suse Linux 10.1 Kurulum | Suse Linux 10.1 Kurulum Sonrası | |
||||||||||||||||||||||||||||||||||||||||||
GTK+'da Fare Olaylarının Düşük Seviyeli Yönetimi
Yasal Uyarı GTK+ bileşenleri (Widget) özellikleriyle ilgili olarak bazı sinyalleri hazır olarak yayarlar. Çoğu durumda bu sinyaller yeterlidir. Hatta GTK+ hakkındaki belgeleri incelerken bu sinyallerden pek çoğuna hiç ihtiyaç duymayacağınızı dahi düşünebilirsiniz. Çoğu zaman yapmanız gereken size zaten hazır olarak sağlanmış sinyale fonksiyonunuzu basitçe bağlamaktır ama bir gün programınızda olmasını istediğiniz bir özelliği gerçekleştirebilmek için ihtiyaç duyduğunuz sinyalin ilgili bileşen tarafından hazır olarak yayılmadığını görürseniz. Artık tam kontrol için sizin de "event" sinyaliyle tanışma ve olayları ele alma zamanınız gelmiştir. Bu yazıda GdkEvent birliği (union GdkEvent) ve fareyle ilgili temel olay yapıları (event struct) açıklanmaya çalışılmıştır. İmrenerek baktığımız profesyonel programlar artık kullanıcılarına pek çok içerik menüsü sağlıyorlar. Sağ tuşla tıkladığımız konuma göre içeriği özelleştirilmiş menüler, kullanıcıların programın ayar veya özelliklerine hızlıca erişmesini sağlıyor. Yine profesyonel programlarda, düşük önem seviyesine sahip bilgilendirme için durum çubuğunun yoğun olarak kullanıldığını görüyoruz. Mesela menülerde gezerken farenin üzerinde bulunduğu seçenekle ilgili birer ikişer cümlelik açıklamalar durum çubuğuna gelip gider. Kendi programlarınızda bunlar gibi özelliklerin bulunmasını isterseniz bileşenlerin sağladağı hazır sinyallerden fazlasına ihtiyacınız var demektir. Çünkü GTK+2'de bileşenler, farenin sağ tuşuyla üzerlerine tıklanması durumunda çağrılmasını istediğiniz fonksiyonunuzu bağlayabileceğiniz hazır bir sinyal sunmazlar. Benzer şekilde menü seçenekleri de fare imleci üzerlerinde dolaşırken buna özel bir sinyal yaymaz. Sorunu bu kadar tanımladıktan sonra şimdi çözüme geçelim. Olay yönetim sistemiyle ilişkilendirilmiş olan bileşenler, kendileriyle ilgili herhangi bir olay meydana geldiğinde "event" sinyalini yayar. (Bazı bileşenler olay yönetim sistemiyle ilişkilendirilmemiştir. Bunların bazıları sadece bir çizim, bir grafiktirler. Mesela GtkImage gibi. Bazılarıysa görünmez bile. Bu tür bileşenlerin üzerinde meydana gelecek olayları yakalamanız gerekiyorsa onları bir GtkEventBox içine koymanız gerekir.) "event" sinyaline bağlamak istediğiniz fonksiyonun prototipi şu şekilde olmalıdır:
gboolean user_function (GtkWidget *widget,
GdkEvent *event,
gpointer user_data)
Birinci parametre sinyali yayan bileşenin adresidir. İkinci parametreyse biraz sonra ayrıntılı olarak ele alacağımız GdkEvent birliğine işaret eder. Bu birlik bize meydana gelen olayla ilgili her türlü bilgiyi sağlar. Üçüncü parametreyse fonksiyonumuzu bağlarken bizim belirttiğimiz ve fonksiyona geçirilmesini istediğimiz herhangi bir verinin adresi olabilir. Geri dönüş değerine gelince, eğer FALSE döndürürsek sinyal yayılmaya devam eder. TRUE değeri döndürürsek sinyalin yayılımını durdururuz. GdkEvent birliğinin bildirimi aşağıdaki gibidir :
union GdkEvent {
GdkEventType type;
GdkEventAny any;
GdkEventExpose expose;
GdkEventNoExpose no_expose;
GdkEventVisibility visibility;
GdkEventMotion motion;
GdkEventButton button;
GdkEventScroll scroll;
GdkEventKey key;
GdkEventCrossing crossing;
GdkEventFocus focus_change;
GdkEventConfigure configure;
GdkEventProperty property;
GdkEventSelection selection;
GdkEventOwnerChange owner_change;
GdkEventProximity proximity;
GdkEventClient client;
GdkEventDND dnd;
GdkEventWindowState window_state;
GdkEventSetting setting;
GdkEventGrabBroken grab_broken;};
GdkEvent, birincisi hariç üyelerininin tamamı yapılardan oluşan bir birliktir. Bu yapılara "olay yapıları" (event struct) denir. Olaylar kendilerine özgü farklı veriler içerecekleri ve birlik aynı anda sadece bir olay için düzenlenmiş olacağından GdkEvent, değişik olay yapılarından oluşmuş bir birlik şeklinde tanımlanmıştır. GdkEvent birliğinin birinci üyesi olan "type" (GdkEventType türünde) bir sayımlama sabitidir (enum) ve ne tür bir olayın meydana geldiğini belirtir. Birliği doğru bir şekilde kullanabilmemiz için hangi olayın meydana geldiğini bilmemiz önemlidir. Birlik üyelerinin hepsinin belleğin aynı adresinden itibaren yerleştirildiğini unutmayınız. Yani birliğe üye yapılardan sadece birine yapacağımız erişim anlamlı sonuçlar verecektir. Birliğin birinci üyesi olan GdkEventType sayımlama sabiti, birlik üyesi diğer yapıların da hepsinin ilk üyesidir. Böylece meydana gelen olayın tipi ne olursa olsun birliğin birinci üyesine doğrudan erişmemiz mümkün kılınmıştır. Aşağıdaki tabloda, GdkEventType'in bildiriminde bulunan fareyle ilgli temel olay türleri, açıklamaları ve hangi olay yapısıyla ilişkilendirildiği gösterilmektedir:
Gördüğünüz gibi fareyle ilgili temel olayların ayrıntıları dört değişik yapıda bulunabiliyor. İmlecin hareket ettirilmesi GdkEventMotion yapısı, düğmelerle ilgili olaylar GdkEvenButton yapısı, kaydırma tekerleği ile ilgili olaylar GdkEventScroll yapısı, imlecin bileşenlerin üzerinde dolaşmasıyla ilgili olaylar da GdkEventCrossing yapısı kullanılarak fonksiyonumuza aktarılıyor. Şimdi bu yapıların içlerinde neler olduğuna bakalım. Dört yapının bildirimi ve alanların açıklamaları aşağıdaki gibidir:
/* GdkEventMotion */
typedef struct {
GdkEventType type; /*Sadece GDK_MOTION_NOTIFY olabilir.*/
GdkWindow *window; /*Sinyali yayan GTK+ bileşeni*/
gint8 send_event;
guint32 time; /*milisaniye cinsinden olayın zamanı*/
gdouble x; /*farenin bileşene göre x koordinatı*/
gdouble y; /*farenin bileşene göre y koordinatı*/
gdouble *axes;
guint state; /*Ctrl, Shift, Alt ve fare düğmelerinin durumları
Ayrıntısı aşağıdadır.*/
gint16 is_hint;
GdkDevice *device;
gdouble x_root, y_root; /*Farenin ekrana göre x ve y
koordinatları*/
} GdkEventMotion;
/* GdkEventButton */
typedef struct {
GdkEventType type; /*GDK_BUTTON_PRESS,
GDK_2BUTTON_PRESS,
GDK_3BUTTON_PRESS,
GDK_BUTTON_RELEASE olay türlerinden
biri olabilir.*/
GdkWindow *window; /*Sinyali yayan GTK+ bileşeni*/
gint8 send_event;
guint32 time; /*milisaniye cinsinden olayın zamanı*/
gdouble x; /*farenin bileşene göre x koordinatı*/
gdouble y; /*farenin bileşene göre y koordinatı*/
gdouble *axes; /*Fare olayları için hep NULL olur.*/
guint state; /*Ctrl, Shift, Alt ve fare düğmelerinin durumları
Ayrıntısı aşağıdadır.*/
guint button; /*Basılan ya da bırakılan fare düğmesi:
1 = Sol Düğme
2 = Orta Düğme
3 = Sağ Düğme*/
GdkDevice *device;
gdouble x_root, y_root; /*Farenin ekrana göre x ve y
koordinatları*/
} GdkEventButton;
/* GdkEventCrossing */
typedef struct {
GdkEventType type; /*GDK_ENTER_NOTIFY,
GDK_LEAVE_NOTIFY olay türlerinden biri olabilir*/
GdkWindow *window; /*Sinyali yayan GTK+ bileşeni*/
gint8 send_event;
GdkWindow *subwindow; /*İmlecin üzerine geldiği ya da
ayrıldığı bileşen*/
guint32 time; /*milisaniye cinsinden olayın zamanı*/
gdouble x; /*farenin bileşene göre x koordinatı*/
gdouble y; /*farenin bileşene göre y koordinatı*/
gdouble x_root; /*Farenin ekrana göre x koordinatı*/
gdouble y_root; /*Farenin ekrana göre y koordinatı*/
GdkCrossingMode mode;
GdkNotifyType detail;
gboolean focus;
guint state; /*Ctrl, Shift, Alt ve fare düğmelerinin durumları
Ayrıntısı aşağıdadır.*/
} GdkEventCrossing;
/* GdkEventScroll */
typedef struct {
GdkEventType type; /*Sadece GDK_SCROLL olabilir*/
GdkWindow *window; /*Sinyali yayan GTK+ bileşeni*/
gint8 send_event;
guint32 time; /*milisaniye cinsinden olayın zamanı*/
gdouble x; /*farenin bileşene göre x koordinatı*/
gdouble y; /*Farenin ekrana göre y koordinatı*/
guint state; /*Ctrl, Shift, Alt ve fare düğmelerinin durumları.
Ayrıntısı aşağıdadır.*/
GdkScrollDirection direction; /*Kaydırma tekerleğinin döndürüldüğü yön:
GDK_SCROLL_UP,
GDK_SCROLL_DOWN,
GDK_SCROLL_LEFT,
GDK_SCROLL_RIGHT değerlerini alabilir.*/
GdkDevice *device;
gdouble x_root, y_root; /*Farenin ekrana göre x ve y
koordinatları*/
} GdkEventScroll;
Ctrl, Alt, Shift ve fare düğmelerinin durumlarını barındıran guint türündeki "state" üyesi, GdkModifierType sayımlama sabitinin üyeleriyle mantıksal işlemlere sokularak kullanılır. Bildirimi şöyledir:
typedef enum {
GDK_SHIFT_MASK = 1 << 0, /* Shift Tuşu */
GDK_LOCK_MASK = 1 << 1, /* Caps Lock Tuşu */
GDK_CONTROL_MASK = 1 << 2, /* Control Tuşu */
GDK_MOD1_MASK = 1 << 3, /* Alt Tuşu */
GDK_MOD2_MASK = 1 << 4,
GDK_MOD3_MASK = 1 << 5,
GDK_MOD4_MASK = 1 << 6,
GDK_MOD5_MASK = 1 << 7,
GDK_BUTTON1_MASK = 1 << 8, /* Sol Fare Düğmesi */
GDK_BUTTON2_MASK = 1 << 9, /* Orta Fare Düğmesi */
GDK_BUTTON3_MASK = 1 << 10, /* Sağ Fare Düğmesi */
GDK_BUTTON4_MASK = 1 << 11, /* 4ncü Fare Düğmesi */
GDK_BUTTON5_MASK = 1 << 12, /* 5nci Fare Düğmesi */
/* The next few modifiers are used by XKB, so we skip to the end.
* Bits 16 - 28 are currently unused, but will eventually
* be used for "virtual modifiers". Bit 29 is used internally.
*/
GDK_RELEASE_MASK = 1 << 30,
GDK_MODIFIER_MASK = GDK_RELEASE_MASK | 0x1fff
} GdkModifierType;
Önce GdkEvent birliğinin üyelerine nasıl erişebileceğimize bakalım. Daha önce bahsettiğimiz gibi özellikli bir durumu olan GdkEventType türündeki "type" üyesine doğrudan erişebiliyoruz:
GdkEvent *event;
GdkEventType type;
type = event->type;
Diğer üyelere erişim için iki ayrı yol kullanabiliriz. Bir örnek üzerinde hareket edelim. Herhangi bir bileşenin üzerine farenin hangi düğmesiyle tıklandığını öğrenmek istediğimizi farzedelim. Temel fare olaylarının verildiği yukarıdaki tabloya bakın. Bir butona tıklandığında, GDK_BUTTON_PRESS olayının gerçekleştiğini ve olayla ilgili ayrıntıları GdkEvent birliğininin, GdkEventButton türünde tanımlı olan "button" üyesi aracılığıyla almamız gerektiğini görürüz. GdkEventButton yapısının bildiriminini verirken basılan tuş bilgisinin "button" üyesinde saklandığını belirtmiştik. Şimdi GdkEvent birliğinin ilgili üyesi aracalığıyla istediğimiz bilgiye erişelim:
GdkEvent *event;
guint btn;
btn = event->button.button;
Aynı sonuca bilinçli tür dönüşümü yaparak aşağıdaki şekilde de ulaşabiliriz:
GdkEvent *event;
guint btn;
btn = ((GdkEventButton*)event)->button;
İnceleyeceğiniz örnek kodlarda her iki yöntemin de kullanıldığını görebilirsiniz. Bana birinci yöntem daha doğal geliyor. Artık yazının başında bahsettiğimiz özelliklere sahip örnek bir program yazabiliriz. Örnek programımız bir ana menü, bir araç çubuğu, bir durum çubuğu ve bir içerik menüsünden oluşmaktadır. Kullanıcı menüde dolaşırken durum çubuğunda, üzerinde bulunulan menü seçeneği hakkında açıklayıcı bir metin görüntüler. Menü seçeneklerinin tamamı araç çubuğunda da vardır. Ancak araç çubuğunda boş bir yere sağ tuşla tıklanırsa içerik menüsü aktifleşir ve kullanıcı araç çubuğundan istemediği düğmeleri bu menü yardımıyla kaldırabilir.
/*
Derlemek İçin :
gcc -o fare1 fare1.c `pkg-config --cflags --libs gtk+-2.0`
*/
#include "gtk/gtk.h"
GtkWidget *Win, *VBox, *MBar, *Lab, *Tbar,
*Sbar, *Menu, *MItem;
GtkToolItem *TINew, *TIOpen, *TISave, *TIQuit;
guint SbarCI;
gboolean cb_win_delete_event (GtkWidget *Widget, GdkEvent Event, gpointer Data)
{
gtk_main_quit ();
return FALSE;
}
gboolean cb_menu_item_event (GtkWidget *Widget, GdkEvent *Event, gpointer Data)
{
if (Event->type == GDK_ENTER_NOTIFY)
gtk_statusbar_push (GTK_STATUSBAR (Sbar), SbarCI, (gchar *) Data);
if (Event->type == GDK_LEAVE_NOTIFY)
gtk_statusbar_pop (GTK_STATUSBAR (Sbar), SbarCI);
return FALSE;
}
gboolean cb_toolbar_event (GtkWidget *Widget, GdkEvent *Event, gpointer Data)
{
if (Event->type == GDK_BUTTON_PRESS)
if (Event->button.button == 3)
{
gtk_menu_popup (GTK_MENU (Menu),
NULL, NULL, NULL, NULL,
Event->button.button, Event->button.time);
return TRUE;
}
return FALSE;
}
void cb_context_menu_toggled (GtkCheckMenuItem *CMItem, GtkWidget *Data)
{
if (gtk_check_menu_item_get_active (CMItem))
gtk_widget_show (Data);
else gtk_widget_hide (Data);
}
int main (int argc, char *argv[])
{
gtk_init (&argc, &argv);
Win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (Win), "Fare Olayları Örneği 1");
gtk_widget_set_size_request (Win, 300, 250);
g_signal_connect (G_OBJECT (Win), "delete_event",
G_CALLBACK (cb_win_delete_event), NULL);
VBox = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (Win), VBox);
MBar = gtk_menu_bar_new ();
gtk_box_pack_start (GTK_BOX (VBox), MBar, FALSE, FALSE, 0);
Tbar = gtk_toolbar_new ();
gtk_box_pack_start (GTK_BOX (VBox), Tbar, FALSE, FALSE, 0);
g_signal_connect (G_OBJECT (Tbar), "event",
G_CALLBACK (cb_toolbar_event), NULL);
Lab = gtk_label_new (NULL);
gtk_label_set_markup (GTK_LABEL (Lab),
"Fare Olayları Örneği\n\
\n\
Araç çubuğunun üzerinde boş bir yere sağ\n\
tıklayarak içerik menüsüne ulaşabilirsiniz.\n\
\n\
Ana menu seçenekleri hakkında bilgilendirme\n\
durum çubuğunda yapılmaktadır.\n");
gtk_box_pack_start (GTK_BOX (VBox), Lab, TRUE, TRUE, 20);
Sbar = gtk_statusbar_new ();
gtk_box_pack_start (GTK_BOX (VBox), Sbar, FALSE, FALSE, 0);
SbarCI = gtk_statusbar_get_context_id (GTK_STATUSBAR (Sbar), "a");
gtk_statusbar_push (GTK_STATUSBAR (Sbar), SbarCI, "Hazır.");
MItem = gtk_menu_item_new_with_mnemonic ("_Dosya");
gtk_menu_shell_append (GTK_MENU_SHELL (MBar), MItem);
Menu = gtk_menu_new ();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (MItem), Menu);
/* Dosya Menüsü */
MItem = gtk_image_menu_item_new_from_stock (GTK_STOCK_NEW, NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (Menu), MItem);
g_signal_connect (G_OBJECT (MItem), "event",
G_CALLBACK (cb_menu_item_event), "Yeni dosya oluştur.");
MItem = gtk_image_menu_item_new_from_stock (GTK_STOCK_OPEN, NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (Menu), MItem);
g_signal_connect (G_OBJECT (MItem), "event",
G_CALLBACK (cb_menu_item_event), "Mevcut bir dosyayı aç.");
MItem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SAVE, NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (Menu), MItem);
g_signal_connect (G_OBJECT (MItem), "event",
G_CALLBACK (cb_menu_item_event), "Dosyayı Kaydet.");
MItem = gtk_image_menu_item_new_from_stock (GTK_STOCK_QUIT, NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (Menu), MItem);
g_signal_connect (G_OBJECT (MItem), "event",
G_CALLBACK (cb_menu_item_event), "Programdan Çık.");
g_signal_connect (G_OBJECT (MItem), "activate",
G_CALLBACK (cb_win_delete_event), NULL);
/* Araç Çubuğu */
TINew = gtk_tool_button_new_from_stock (GTK_STOCK_NEW);
gtk_toolbar_insert (GTK_TOOLBAR (Tbar), TINew, -1);
TIOpen = gtk_tool_button_new_from_stock (GTK_STOCK_OPEN);
gtk_toolbar_insert (GTK_TOOLBAR (Tbar), TIOpen, -1);
TISave = gtk_tool_button_new_from_stock (GTK_STOCK_SAVE);
gtk_toolbar_insert (GTK_TOOLBAR (Tbar), TISave, -1);
TIQuit = gtk_tool_button_new_from_stock (GTK_STOCK_QUIT);
gtk_toolbar_insert (GTK_TOOLBAR (Tbar), TIQuit, -1);
g_signal_connect (G_OBJECT (TIQuit), "clicked",
G_CALLBACK (cb_win_delete_event), NULL);
/* İçerik Menüsü */
Menu = gtk_menu_new ();
MItem = gtk_menu_item_new_with_label ("Araç Çubuğunda Göster");
gtk_menu_shell_append (GTK_MENU_SHELL (Menu), MItem);
gtk_widget_set_sensitive (MItem, FALSE);
gtk_widget_show (MItem);
MItem = gtk_check_menu_item_new_with_label ("Yeni");
gtk_menu_shell_append (GTK_MENU_SHELL (Menu), MItem);
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (MItem), TRUE);
g_signal_connect (G_OBJECT (MItem), "toggled",
G_CALLBACK (cb_context_menu_toggled), TINew);
gtk_widget_show (MItem);
MItem = gtk_check_menu_item_new_with_label ("Aç");
gtk_menu_shell_append (GTK_MENU_SHELL (Menu), MItem);
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (MItem), TRUE);
g_signal_connect (G_OBJECT (MItem), "toggled",
G_CALLBACK (cb_context_menu_toggled), TIOpen);
gtk_widget_show (MItem);
MItem = gtk_check_menu_item_new_with_label ("Kaydet");
gtk_menu_shell_append (GTK_MENU_SHELL (Menu), MItem);
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (MItem), TRUE);
g_signal_connect (G_OBJECT (MItem), "toggled",
G_CALLBACK (cb_context_menu_toggled), TISave);
gtk_widget_show (MItem);
MItem = gtk_check_menu_item_new_with_label ("Çık");
gtk_menu_shell_append (GTK_MENU_SHELL (Menu), MItem);
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (MItem), TRUE);
g_signal_connect (G_OBJECT (MItem), "toggled",
G_CALLBACK (cb_context_menu_toggled), TIQuit);
gtk_widget_show (MItem);
gtk_widget_show_all (Win);
gtk_main ();
return 0;
}
|
||||||||||||||||||||||||||||||||||||||||||
Telif Hakkı (c) 2007,2008,2009 Ahmet Zeki EYMÜR
|
||||||||||||||||||||||||||||||||||||||||||