1// Same copyright and license as the rest of the files in this project 2// This file contains accelerator related functions and structures 3 4package gtk 5 6// #include <gtk/gtk.h> 7// #include "gtk.go.h" 8import "C" 9import ( 10 "errors" 11 "unsafe" 12 13 "github.com/gotk3/gotk3/gdk" 14 "github.com/gotk3/gotk3/glib" 15) 16 17/* 18 * GtkWidget 19 */ 20 21// Widget is a representation of GTK's GtkWidget. 22type Widget struct { 23 glib.InitiallyUnowned 24} 25 26// IWidget is an interface type implemented by all structs 27// embedding a Widget. It is meant to be used as an argument type 28// for wrapper functions that wrap around a C GTK function taking a 29// GtkWidget. 30type IWidget interface { 31 toWidget() *C.GtkWidget 32 Set(string, interface{}) error 33} 34 35type IWidgetable interface { 36 toWidget() *C.GtkWidget 37} 38 39func nullableWidget(v IWidgetable) *C.GtkWidget { 40 if v == nil { 41 return nil 42 } 43 44 return v.toWidget() 45} 46 47// native returns a pointer to the underlying GtkWidget. 48func (v *Widget) native() *C.GtkWidget { 49 if v == nil || v.GObject == nil { 50 return nil 51 } 52 p := unsafe.Pointer(v.GObject) 53 return C.toGtkWidget(p) 54} 55 56func (v *Widget) toWidget() *C.GtkWidget { 57 if v == nil { 58 return nil 59 } 60 return v.native() 61} 62 63func marshalWidget(p uintptr) (interface{}, error) { 64 c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) 65 obj := glib.Take(unsafe.Pointer(c)) 66 return wrapWidget(obj), nil 67} 68 69func wrapWidget(obj *glib.Object) *Widget { 70 return &Widget{glib.InitiallyUnowned{obj}} 71} 72 73// Destroy is a wrapper around gtk_widget_destroy(). 74func (v *Widget) Destroy() { 75 C.gtk_widget_destroy(v.native()) 76} 77 78func (v *Widget) HideOnDelete() { 79 C._gtk_widget_hide_on_delete(v.native()) 80} 81 82func (v *Widget) DragDestSet(flags DestDefaults, targets []TargetEntry, actions gdk.DragAction) { 83 C.gtk_drag_dest_set(v.native(), C.GtkDestDefaults(flags), (*C.GtkTargetEntry)(&targets[0]), C.gint(len(targets)), C.GdkDragAction(actions)) 84} 85 86// ResetStyle is a wrapper around gtk_widget_reset_style(). 87func (v *Widget) ResetStyle() { 88 C.gtk_widget_reset_style(v.native()) 89} 90 91// InDestruction is a wrapper around gtk_widget_in_destruction(). 92func (v *Widget) InDestruction() bool { 93 return gobool(C.gtk_widget_in_destruction(v.native())) 94} 95 96// TODO(jrick) this may require some rethinking 97/* 98func (v *Widget) Destroyed(widgetPointer **Widget) { 99} 100*/ 101 102// Unparent is a wrapper around gtk_widget_unparent(). 103func (v *Widget) Unparent() { 104 C.gtk_widget_unparent(v.native()) 105} 106 107// Show is a wrapper around gtk_widget_show(). 108func (v *Widget) Show() { 109 C.gtk_widget_show(v.native()) 110} 111 112// Hide is a wrapper around gtk_widget_hide(). 113func (v *Widget) Hide() { 114 C.gtk_widget_hide(v.native()) 115} 116 117// GetCanFocus is a wrapper around gtk_widget_get_can_focus(). 118func (v *Widget) GetCanFocus() bool { 119 c := C.gtk_widget_get_can_focus(v.native()) 120 return gobool(c) 121} 122 123// SetCanFocus is a wrapper around gtk_widget_set_can_focus(). 124func (v *Widget) SetCanFocus(canFocus bool) { 125 C.gtk_widget_set_can_focus(v.native(), gbool(canFocus)) 126} 127 128// GetCanDefault is a wrapper around gtk_widget_get_can_default(). 129func (v *Widget) GetCanDefault() bool { 130 c := C.gtk_widget_get_can_default(v.native()) 131 return gobool(c) 132} 133 134// SetCanDefault is a wrapper around gtk_widget_set_can_default(). 135func (v *Widget) SetCanDefault(canDefault bool) { 136 C.gtk_widget_set_can_default(v.native(), gbool(canDefault)) 137} 138 139// GetMapped is a wrapper around gtk_widget_get_mapped(). 140func (v *Widget) GetMapped() bool { 141 c := C.gtk_widget_get_mapped(v.native()) 142 return gobool(c) 143} 144 145// SetMapped is a wrapper around gtk_widget_set_mapped(). 146func (v *Widget) SetMapped(mapped bool) { 147 C.gtk_widget_set_can_focus(v.native(), gbool(mapped)) 148} 149 150// GetRealized is a wrapper around gtk_widget_get_realized(). 151func (v *Widget) GetRealized() bool { 152 c := C.gtk_widget_get_realized(v.native()) 153 return gobool(c) 154} 155 156// SetRealized is a wrapper around gtk_widget_set_realized(). 157func (v *Widget) SetRealized(realized bool) { 158 C.gtk_widget_set_realized(v.native(), gbool(realized)) 159} 160 161// GetHasWindow is a wrapper around gtk_widget_get_has_window(). 162func (v *Widget) GetHasWindow() bool { 163 c := C.gtk_widget_get_has_window(v.native()) 164 return gobool(c) 165} 166 167// SetHasWindow is a wrapper around gtk_widget_set_has_window(). 168func (v *Widget) SetHasWindow(hasWindow bool) { 169 C.gtk_widget_set_has_window(v.native(), gbool(hasWindow)) 170} 171 172// ShowNow is a wrapper around gtk_widget_show_now(). 173func (v *Widget) ShowNow() { 174 C.gtk_widget_show_now(v.native()) 175} 176 177// ShowAll is a wrapper around gtk_widget_show_all(). 178func (v *Widget) ShowAll() { 179 C.gtk_widget_show_all(v.native()) 180} 181 182// SetNoShowAll is a wrapper around gtk_widget_set_no_show_all(). 183func (v *Widget) SetNoShowAll(noShowAll bool) { 184 C.gtk_widget_set_no_show_all(v.native(), gbool(noShowAll)) 185} 186 187// GetNoShowAll is a wrapper around gtk_widget_get_no_show_all(). 188func (v *Widget) GetNoShowAll() bool { 189 c := C.gtk_widget_get_no_show_all(v.native()) 190 return gobool(c) 191} 192 193// Map is a wrapper around gtk_widget_map(). 194func (v *Widget) Map() { 195 C.gtk_widget_map(v.native()) 196} 197 198// Unmap is a wrapper around gtk_widget_unmap(). 199func (v *Widget) Unmap() { 200 C.gtk_widget_unmap(v.native()) 201} 202 203// QueueDrawArea is a wrapper aroung gtk_widget_queue_draw_area(). 204func (v *Widget) QueueDrawArea(x, y, w, h int) { 205 C.gtk_widget_queue_draw_area(v.native(), C.gint(x), C.gint(y), C.gint(w), C.gint(h)) 206} 207 208//void gtk_widget_realize(GtkWidget *widget); 209//void gtk_widget_unrealize(GtkWidget *widget); 210//void gtk_widget_draw(GtkWidget *widget, cairo_t *cr); 211//void gtk_widget_queue_resize(GtkWidget *widget); 212//void gtk_widget_queue_resize_no_redraw(GtkWidget *widget); 213//GdkFrameClock *gtk_widget_get_frame_clock(GtkWidget *widget); 214//guint gtk_widget_add_tick_callback (GtkWidget *widget, 215// GtkTickCallback callback, 216// gpointer user_data, 217// GDestroyNotify notify); 218//void gtk_widget_remove_tick_callback(GtkWidget *widget, guint id); 219 220// TODO(jrick) GtkAllocation 221/* 222func (v *Widget) SizeAllocate() { 223} 224*/ 225 226// Allocation is a representation of GTK's GtkAllocation type. 227type Allocation struct { 228 gdk.Rectangle 229} 230 231// Native returns a pointer to the underlying GtkAllocation. 232func (v *Allocation) native() *C.GtkAllocation { 233 return (*C.GtkAllocation)(unsafe.Pointer(&v.GdkRectangle)) 234} 235 236// GetAllocatedWidth() is a wrapper around gtk_widget_get_allocated_width(). 237func (v *Widget) GetAllocatedWidth() int { 238 return int(C.gtk_widget_get_allocated_width(v.native())) 239} 240 241// GetAllocatedHeight() is a wrapper around gtk_widget_get_allocated_height(). 242func (v *Widget) GetAllocatedHeight() int { 243 return int(C.gtk_widget_get_allocated_height(v.native())) 244} 245 246// Event() is a wrapper around gtk_widget_event(). 247func (v *Widget) Event(event *gdk.Event) bool { 248 c := C.gtk_widget_event(v.native(), 249 (*C.GdkEvent)(unsafe.Pointer(event.Native()))) 250 return gobool(c) 251} 252 253// Activate() is a wrapper around gtk_widget_activate(). 254func (v *Widget) Activate() bool { 255 return gobool(C.gtk_widget_activate(v.native())) 256} 257 258// TODO(jrick) GdkRectangle 259/* 260func (v *Widget) Intersect() { 261} 262*/ 263 264// IsFocus() is a wrapper around gtk_widget_is_focus(). 265func (v *Widget) IsFocus() bool { 266 return gobool(C.gtk_widget_is_focus(v.native())) 267} 268 269// GrabFocus() is a wrapper around gtk_widget_grab_focus(). 270func (v *Widget) GrabFocus() { 271 C.gtk_widget_grab_focus(v.native()) 272} 273 274// GrabDefault() is a wrapper around gtk_widget_grab_default(). 275func (v *Widget) GrabDefault() { 276 C.gtk_widget_grab_default(v.native()) 277} 278 279// SetName() is a wrapper around gtk_widget_set_name(). 280func (v *Widget) SetName(name string) { 281 cstr := C.CString(name) 282 defer C.free(unsafe.Pointer(cstr)) 283 C.gtk_widget_set_name(v.native(), (*C.gchar)(cstr)) 284} 285 286// GetName() is a wrapper around gtk_widget_get_name(). A non-nil 287// error is returned in the case that gtk_widget_get_name returns NULL to 288// differentiate between NULL and an empty string. 289func (v *Widget) GetName() (string, error) { 290 c := C.gtk_widget_get_name(v.native()) 291 if c == nil { 292 return "", nilPtrErr 293 } 294 return C.GoString((*C.char)(c)), nil 295} 296 297// GetSensitive is a wrapper around gtk_widget_get_sensitive(). 298func (v *Widget) GetSensitive() bool { 299 c := C.gtk_widget_get_sensitive(v.native()) 300 return gobool(c) 301} 302 303// IsSensitive is a wrapper around gtk_widget_is_sensitive(). 304func (v *Widget) IsSensitive() bool { 305 c := C.gtk_widget_is_sensitive(v.native()) 306 return gobool(c) 307} 308 309// SetSensitive is a wrapper around gtk_widget_set_sensitive(). 310func (v *Widget) SetSensitive(sensitive bool) { 311 C.gtk_widget_set_sensitive(v.native(), gbool(sensitive)) 312} 313 314// GetVisible is a wrapper around gtk_widget_get_visible(). 315func (v *Widget) GetVisible() bool { 316 c := C.gtk_widget_get_visible(v.native()) 317 return gobool(c) 318} 319 320// SetVisible is a wrapper around gtk_widget_set_visible(). 321func (v *Widget) SetVisible(visible bool) { 322 C.gtk_widget_set_visible(v.native(), gbool(visible)) 323} 324 325// SetParent is a wrapper around gtk_widget_set_parent(). 326func (v *Widget) SetParent(parent IWidget) { 327 C.gtk_widget_set_parent(v.native(), parent.toWidget()) 328} 329 330// GetParent is a wrapper around gtk_widget_get_parent(). 331func (v *Widget) GetParent() (*Widget, error) { 332 c := C.gtk_widget_get_parent(v.native()) 333 if c == nil { 334 return nil, nilPtrErr 335 } 336 return wrapWidget(glib.Take(unsafe.Pointer(c))), nil 337} 338 339// SetSizeRequest is a wrapper around gtk_widget_set_size_request(). 340func (v *Widget) SetSizeRequest(width, height int) { 341 C.gtk_widget_set_size_request(v.native(), C.gint(width), C.gint(height)) 342} 343 344// GetSizeRequest is a wrapper around gtk_widget_get_size_request(). 345func (v *Widget) GetSizeRequest() (width, height int) { 346 var w, h C.gint 347 C.gtk_widget_get_size_request(v.native(), &w, &h) 348 return int(w), int(h) 349} 350 351// SetParentWindow is a wrapper around gtk_widget_set_parent_window(). 352func (v *Widget) SetParentWindow(parentWindow *gdk.Window) { 353 C.gtk_widget_set_parent_window(v.native(), 354 (*C.GdkWindow)(unsafe.Pointer(parentWindow.Native()))) 355} 356 357// GetParentWindow is a wrapper around gtk_widget_get_parent_window(). 358func (v *Widget) GetParentWindow() (*gdk.Window, error) { 359 c := C.gtk_widget_get_parent_window(v.native()) 360 if v == nil { 361 return nil, nilPtrErr 362 } 363 364 w := &gdk.Window{glib.Take(unsafe.Pointer(c))} 365 return w, nil 366} 367 368// SetEvents is a wrapper around gtk_widget_set_events(). 369func (v *Widget) SetEvents(events int) { 370 C.gtk_widget_set_events(v.native(), C.gint(events)) 371} 372 373// GetEvents is a wrapper around gtk_widget_get_events(). 374func (v *Widget) GetEvents() int { 375 return int(C.gtk_widget_get_events(v.native())) 376} 377 378// AddEvents is a wrapper around gtk_widget_add_events(). 379func (v *Widget) AddEvents(events int) { 380 C.gtk_widget_add_events(v.native(), C.gint(events)) 381} 382 383// FreezeChildNotify is a wrapper around gtk_widget_freeze_child_notify(). 384func (v *Widget) FreezeChildNotify() { 385 C.gtk_widget_freeze_child_notify(v.native()) 386} 387 388// ThawChildNotify is a wrapper around gtk_widget_thaw_child_notify(). 389func (v *Widget) ThawChildNotify() { 390 C.gtk_widget_thaw_child_notify(v.native()) 391} 392 393// HasDefault is a wrapper around gtk_widget_has_default(). 394func (v *Widget) HasDefault() bool { 395 c := C.gtk_widget_has_default(v.native()) 396 return gobool(c) 397} 398 399// HasFocus is a wrapper around gtk_widget_has_focus(). 400func (v *Widget) HasFocus() bool { 401 c := C.gtk_widget_has_focus(v.native()) 402 return gobool(c) 403} 404 405// HasVisibleFocus is a wrapper around gtk_widget_has_visible_focus(). 406func (v *Widget) HasVisibleFocus() bool { 407 c := C.gtk_widget_has_visible_focus(v.native()) 408 return gobool(c) 409} 410 411// HasGrab is a wrapper around gtk_widget_has_grab(). 412func (v *Widget) HasGrab() bool { 413 c := C.gtk_widget_has_grab(v.native()) 414 return gobool(c) 415} 416 417// IsDrawable is a wrapper around gtk_widget_is_drawable(). 418func (v *Widget) IsDrawable() bool { 419 c := C.gtk_widget_is_drawable(v.native()) 420 return gobool(c) 421} 422 423// IsToplevel is a wrapper around gtk_widget_is_toplevel(). 424func (v *Widget) IsToplevel() bool { 425 c := C.gtk_widget_is_toplevel(v.native()) 426 return gobool(c) 427} 428 429// TODO(jrick) GdkEventMask 430/* 431func (v *Widget) SetDeviceEvents() { 432} 433*/ 434 435// TODO(jrick) GdkEventMask 436/* 437func (v *Widget) GetDeviceEvents() { 438} 439*/ 440 441// TODO(jrick) GdkEventMask 442/* 443func (v *Widget) AddDeviceEvents() { 444} 445*/ 446 447// SetDeviceEnabled is a wrapper around gtk_widget_set_device_enabled(). 448func (v *Widget) SetDeviceEnabled(device *gdk.Device, enabled bool) { 449 C.gtk_widget_set_device_enabled(v.native(), 450 (*C.GdkDevice)(unsafe.Pointer(device.Native())), gbool(enabled)) 451} 452 453// GetDeviceEnabled is a wrapper around gtk_widget_get_device_enabled(). 454func (v *Widget) GetDeviceEnabled(device *gdk.Device) bool { 455 c := C.gtk_widget_get_device_enabled(v.native(), 456 (*C.GdkDevice)(unsafe.Pointer(device.Native()))) 457 return gobool(c) 458} 459 460// GetToplevel is a wrapper around gtk_widget_get_toplevel(). 461func (v *Widget) GetToplevel() (*Widget, error) { 462 c := C.gtk_widget_get_toplevel(v.native()) 463 if c == nil { 464 return nil, nilPtrErr 465 } 466 return wrapWidget(glib.Take(unsafe.Pointer(c))), nil 467} 468 469// GetTooltipText is a wrapper around gtk_widget_get_tooltip_text(). 470// A non-nil error is returned in the case that 471// gtk_widget_get_tooltip_text returns NULL to differentiate between NULL 472// and an empty string. 473func (v *Widget) GetTooltipText() (string, error) { 474 c := C.gtk_widget_get_tooltip_text(v.native()) 475 if c == nil { 476 return "", nilPtrErr 477 } 478 return C.GoString((*C.char)(c)), nil 479} 480 481// SetTooltipText is a wrapper around gtk_widget_set_tooltip_text(). 482func (v *Widget) SetTooltipText(text string) { 483 cstr := C.CString(text) 484 defer C.free(unsafe.Pointer(cstr)) 485 C.gtk_widget_set_tooltip_text(v.native(), (*C.gchar)(cstr)) 486} 487 488// GetHAlign is a wrapper around gtk_widget_get_halign(). 489func (v *Widget) GetHAlign() Align { 490 c := C.gtk_widget_get_halign(v.native()) 491 return Align(c) 492} 493 494// SetHAlign is a wrapper around gtk_widget_set_halign(). 495func (v *Widget) SetHAlign(align Align) { 496 C.gtk_widget_set_halign(v.native(), C.GtkAlign(align)) 497} 498 499// GetVAlign is a wrapper around gtk_widget_get_valign(). 500func (v *Widget) GetVAlign() Align { 501 c := C.gtk_widget_get_valign(v.native()) 502 return Align(c) 503} 504 505// SetVAlign is a wrapper around gtk_widget_set_valign(). 506func (v *Widget) SetVAlign(align Align) { 507 C.gtk_widget_set_valign(v.native(), C.GtkAlign(align)) 508} 509 510// GetMarginTop is a wrapper around gtk_widget_get_margin_top(). 511func (v *Widget) GetMarginTop() int { 512 c := C.gtk_widget_get_margin_top(v.native()) 513 return int(c) 514} 515 516// SetMarginTop is a wrapper around gtk_widget_set_margin_top(). 517func (v *Widget) SetMarginTop(margin int) { 518 C.gtk_widget_set_margin_top(v.native(), C.gint(margin)) 519} 520 521// GetMarginBottom is a wrapper around gtk_widget_get_margin_bottom(). 522func (v *Widget) GetMarginBottom() int { 523 c := C.gtk_widget_get_margin_bottom(v.native()) 524 return int(c) 525} 526 527// SetMarginBottom is a wrapper around gtk_widget_set_margin_bottom(). 528func (v *Widget) SetMarginBottom(margin int) { 529 C.gtk_widget_set_margin_bottom(v.native(), C.gint(margin)) 530} 531 532// GetHExpand is a wrapper around gtk_widget_get_hexpand(). 533func (v *Widget) GetHExpand() bool { 534 c := C.gtk_widget_get_hexpand(v.native()) 535 return gobool(c) 536} 537 538// SetHExpand is a wrapper around gtk_widget_set_hexpand(). 539func (v *Widget) SetHExpand(expand bool) { 540 C.gtk_widget_set_hexpand(v.native(), gbool(expand)) 541} 542 543// GetVExpand is a wrapper around gtk_widget_get_vexpand(). 544func (v *Widget) GetVExpand() bool { 545 c := C.gtk_widget_get_vexpand(v.native()) 546 return gobool(c) 547} 548 549// SetVExpand is a wrapper around gtk_widget_set_vexpand(). 550func (v *Widget) SetVExpand(expand bool) { 551 C.gtk_widget_set_vexpand(v.native(), gbool(expand)) 552} 553 554// TranslateCoordinates is a wrapper around gtk_widget_translate_coordinates(). 555func (v *Widget) TranslateCoordinates(dest IWidget, srcX, srcY int) (destX, destY int, e error) { 556 cdest := nullableWidget(dest) 557 558 var cdestX, cdestY C.gint 559 c := C.gtk_widget_translate_coordinates(v.native(), cdest, C.gint(srcX), C.gint(srcY), &cdestX, &cdestY) 560 if !gobool(c) { 561 return 0, 0, errors.New("translate coordinates failed") 562 } 563 return int(cdestX), int(cdestY), nil 564} 565 566// SetVisual is a wrapper around gtk_widget_set_visual(). 567func (v *Widget) SetVisual(visual *gdk.Visual) { 568 C.gtk_widget_set_visual(v.native(), 569 (*C.GdkVisual)(unsafe.Pointer(visual.Native()))) 570} 571 572// SetAppPaintable is a wrapper around gtk_widget_set_app_paintable(). 573func (v *Widget) SetAppPaintable(paintable bool) { 574 C.gtk_widget_set_app_paintable(v.native(), gbool(paintable)) 575} 576 577// GetAppPaintable is a wrapper around gtk_widget_get_app_paintable(). 578func (v *Widget) GetAppPaintable() bool { 579 c := C.gtk_widget_get_app_paintable(v.native()) 580 return gobool(c) 581} 582 583// QueueDraw is a wrapper around gtk_widget_queue_draw(). 584func (v *Widget) QueueDraw() { 585 C.gtk_widget_queue_draw(v.native()) 586} 587 588// GetAllocation is a wrapper around gtk_widget_get_allocation(). 589func (v *Widget) GetAllocation() *Allocation { 590 var a Allocation 591 C.gtk_widget_get_allocation(v.native(), a.native()) 592 return &a 593} 594 595// SetAllocation is a wrapper around gtk_widget_set_allocation(). 596func (v *Widget) SetAllocation(allocation *Allocation) { 597 C.gtk_widget_set_allocation(v.native(), allocation.native()) 598} 599 600// SizeAllocate is a wrapper around gtk_widget_size_allocate(). 601func (v *Widget) SizeAllocate(allocation *Allocation) { 602 C.gtk_widget_size_allocate(v.native(), allocation.native()) 603} 604 605// SetStateFlags is a wrapper around gtk_widget_set_state_flags(). 606func (v *Widget) SetStateFlags(stateFlags StateFlags, clear bool) { 607 C.gtk_widget_set_state_flags(v.native(), C.GtkStateFlags(stateFlags), gbool(clear)) 608} 609 610// GetWindow is a wrapper around gtk_widget_get_window(). 611func (v *Widget) GetWindow() (*gdk.Window, error) { 612 c := C.gtk_widget_get_window(v.native()) 613 if c == nil { 614 return nil, nilPtrErr 615 } 616 617 w := &gdk.Window{glib.Take(unsafe.Pointer(c))} 618 return w, nil 619} 620 621// GetPreferredHeight is a wrapper around gtk_widget_get_preferred_height(). 622func (v *Widget) GetPreferredHeight() (int, int) { 623 var minimum, natural C.gint 624 C.gtk_widget_get_preferred_height(v.native(), &minimum, &natural) 625 return int(minimum), int(natural) 626} 627 628// GetPreferredWidth is a wrapper around gtk_widget_get_preferred_width(). 629func (v *Widget) GetPreferredWidth() (int, int) { 630 var minimum, natural C.gint 631 C.gtk_widget_get_preferred_width(v.native(), &minimum, &natural) 632 return int(minimum), int(natural) 633} 634 635func (v *Widget) InsertActionGroup(name string, group glib.IActionGroup) { 636 C.gtk_widget_insert_action_group(v.native(), (*C.gchar)(C.CString(name)), C.toGActionGroup(unsafe.Pointer(group.Native()))) 637} 638 639// GetScreen is a wrapper around gtk_widget_get_screen(). 640func (v *Widget) GetScreen() (*gdk.Screen, error) { 641 c := C.gtk_widget_get_screen(v.native()) 642 if c == nil { 643 return nil, nilPtrErr 644 } 645 s := &gdk.Screen{glib.Take(unsafe.Pointer(c))} 646 return s, nil 647} 648