(* Tux Commander - UGnome - libgnome and libgnomeui related functions and classes Copyright (C) 2008 Tomas Bzatek Check for updates on tuxcmd.sourceforge.net This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *) unit UGnome; interface uses glib2, gdk2, gdk2pixbuf, gtk2, Classes, ULibc, GTKForms, GTKControls, GTKStdCtrls, GTKExtCtrls, GTKClasses, GTKDialogs, GTKUtils, GTKConsts, uVFSprototypes; type TGnomeColorButton = class(TGTKButton) private FUseGnomeUI: boolean; FColor: TGDKColor; FColorChanged: TNotifyEvent; function GetColor: TGDKColor; procedure SetColor(Value: TGDKColor); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure SetDefaultColor; procedure UnsetDefaultColor; property Color: GTKClasses.TGDKColor read GetColor write SetColor; published property OnColorChanged: TNotifyEvent read FColorChanged write FColorChanged; end; TGnomeIconEntry = class(TGTKVBox) private FIconChanged: TNotifyEvent; function GetFilename: string; procedure SetFilename(Value: string); procedure SetPixmapSubdir(Value: string); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; published property Filename: string read GetFilename write SetFilename; property PixpamSubdir: string write SetPixmapSubdir; property OnIconChanged: TNotifyEvent read FIconChanged write FIconChanged; end; TGnomeDateEdit = class(TGTKHBox) private function GetTime: time_t; procedure SetTime(Value: time_t); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; published property Time: time_t read GetTime write SetTime; end; TEphyNotebook = class; TEphyNotebookReorderedEvent = procedure (Sender: TObject; const Source, Dest: integer) of object; TEphyNotebookTabCloseEvent = procedure (Sender: TObject; const TabNum: integer; var CanClose: boolean) of object; TEphyNotebookTabDoubleClickEvent = procedure (Sender: TObject; const TabNum: integer) of object; TEphyNotebookFindNotebookAtPointerEvent = function (Sender: TObject; const AbsX, AbsY: integer): TEphyNotebook of object; TEphyNotebookMoveTabToAnotherNotebookEvent = function (Sender: TObject; Destination: TEphyNotebook; const SourceTabNo, DestTabNo: integer): boolean of object; TEphyNotebookTabSwitchedEvent = procedure (Sender: TObject; const NewTabNum: integer; const ShouldFocus: boolean) of object; TEphyNotebookTabFocusOnlyEvent = procedure (Sender: TObject; const NewTabNum: integer) of object; // Extended capatibilities notebook (icons, close buttons, reordering by drag&drop) TEphyNotebook = class(TGTKNotebook) private FShowCloseButtons: boolean; FShowTooltips: boolean; FAllowDragDrop: boolean; FAllowDragOutside: boolean; FTooltips: PGtkTooltips; FOnNotebookReordered: TEphyNotebookReorderedEvent; FOnTabClose: TEphyNotebookTabCloseEvent; FOnTabDoubleClick: TEphyNotebookTabDoubleClickEvent; FOnFindNotebookAtPointer: TEphyNotebookFindNotebookAtPointerEvent; FOnMoveTabToAnotherNotebook: TEphyNotebookMoveTabToAnotherNotebookEvent; FOnTabSwitchedEvent: TEphyNotebookTabSwitchedEvent; FOnTabFocusOnlyEvent: TEphyNotebookTabFocusOnlyEvent; // Notebook private motion_notify_handler_id, grab_notify_handler_id, toplevel_grab_broken_handler_id, toplevel_motion_notify_handler_id, toplevel_button_release_handler_id: gulong; x_start, y_start, drag_start_idx: gint; drag_in_progress: gboolean; cursor: PGdkCursor; FBusy: boolean; procedure SetShowCloseButtons(Value: boolean); procedure SetShowTooltips(Value: boolean); procedure SetAllowDragDrop(Value: boolean); procedure SetAllowDragOutside(Value: boolean); function find_tab_num_at_pos(abs_x, abs_y: integer): integer; function find_notebook_at_pointer(abs_x, abs_y: integer): TEphyNotebook; function is_in_notebook_window(notebook: TEphyNotebook; abs_x, abs_y: integer): boolean; function drag_start(time: guint32): gboolean; procedure drag_stop(time: guint32); procedure move_tab(dest_position: integer); procedure move_tab_to_another_notebook(dest: TEphyNotebook; event: PGdkEventMotion); function GetPageIndex: integer; procedure SetPageIndex(Value: integer); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; function AppendPage(Child: TGTKControl; Caption: string): integer; function InsertPage(Position: integer; Child: TGTKControl; Caption: string): integer; procedure RemovePage(PageNo: integer); function GetTabCaption(PageNo: integer): string; procedure SetTabCaption(PageNo: integer; Caption: string); procedure SetTabTooltip(PageNo: integer; Tooltip: string); published property PageIndex: integer read GetPageIndex write SetPageIndex; property ShowCloseButtons: boolean read FShowCloseButtons write SetShowCloseButtons default False; property ShowTooltips: boolean read FShowTooltips write SetShowTooltips default False; property AllowDragDrop: boolean read FAllowDragDrop write SetAllowDragDrop default False; property AllowDragOutside: boolean read FAllowDragOutside write SetAllowDragOutside default False; property OnNotebookReordered: TEphyNotebookReorderedEvent read FOnNotebookReordered write FOnNotebookReordered; property OnTabClose: TEphyNotebookTabCloseEvent read FOnTabClose write FOnTabClose; property OnTabDoubleClick: TEphyNotebookTabDoubleClickEvent read FOnTabDoubleClick write FOnTabDoubleClick; property OnFindNotebookAtPointer: TEphyNotebookFindNotebookAtPointerEvent read FOnFindNotebookAtPointer write FOnFindNotebookAtPointer; property OnMoveTabToAnotherNotebook: TEphyNotebookMoveTabToAnotherNotebookEvent read FOnMoveTabToAnotherNotebook write FOnMoveTabToAnotherNotebook; property OnTabSwitched: TEphyNotebookTabSwitchedEvent read FOnTabSwitchedEvent write FOnTabSwitchedEvent; property OnTabFocusOnlyEvent: TEphyNotebookTabFocusOnlyEvent read FOnTabFocusOnlyEvent write FOnTabFocusOnlyEvent; end; function MessageBoxShowOnce(ParentWindow: PGtkWindow; const Text: string; const DontShowAgainText: string; var DontShowAgainChecked: boolean; Buttons: TMessageButtons = [mbOK]; Style: TMessageStyle = mbInfo; Default: TMessageButton = mbNone; Escape: TMessageButton = mbNone): TMessageButton; procedure HandleVFSAskQuestionCallback(DialogParent: PGtkWidget; const AMessage: PChar; const Choices: PPChar; choice: PInteger); function HandleVFSAskPasswordCallback(DialogParent: PGtkWidget; const AMessage: PChar; const default_user: PChar; const default_domain: PChar; const default_password: PChar; flags: TVFSAskPasswordFlags; username: PPChar; password: PPChar; anonymous: PInteger; domain: PPChar; password_save: PVFSPasswordSave): LongBool; type PGnomeColorPicker = PGtkWidget; PGnomeIconEntry = PGtkWidget; PGnomeDateEdit = PGtkWidget; const AFTER_ALL_TABS = -1; NOT_IN_APP_WINDOWS = -2; var libGlib2Handle, libGtk2Handle, libGnome2Handle, libGnomeUI2Handle: Pointer; gnome_about_new: function (const name, version, copyright, comments: Pchar; const authors, documenters: PPchar; const translator_credits: Pchar; logo_pixbuf: PGdkPixbuf): PGtkWidget; cdecl; {$IFDEF KYLIX} gnome_program_init: function (const app_id, app_version: PChar; const module_info: Pointer; argc: longint; argv: PPChar; const first_property_name: PChar; const first_property_value: Int64; const second_property_name: PChar): Pointer; cdecl; {$ELSE} gnome_program_init: function (const app_id, app_version: PChar; const module_info: Pointer; argc: integer; argv: PPChar): Pointer; varargs; cdecl; {$ENDIF} libgnome_module_info_get: function: Pointer; cdecl; libgnomeui_module_info_get: function: Pointer; cdecl; gnome_color_picker_new: function: PGnomeColorPicker; cdecl; gnome_color_picker_get_i16: procedure (cp: PGnomeColorPicker; R, G, B, A: Pword); cdecl; gnome_color_picker_set_i16: procedure (cp: PGnomeColorPicker; R, G, B, A: word); cdecl; gnome_icon_entry_new: function (const history_id, browse_dialog_title: Pchar): PGtkWidget; cdecl; gnome_icon_entry_set_pixmap_subdir: procedure (ientry: PGnomeIconEntry; const subdir: Pgchar); cdecl; gnome_icon_entry_get_filename: function (ientry: PGnomeIconEntry): Pgchar; cdecl; gnome_icon_entry_set_filename: function (ientry: PGnomeIconEntry; const filename: Pgchar): gboolean; cdecl; gnome_date_edit_new: function (the_time: time_t; show_time, use_24_format: gboolean): PGtkWidget; cdecl; gnome_date_edit_set_time: procedure (gde: PGnomeDateEdit; the_time: time_t); cdecl; gnome_date_edit_get_time: function (gde: PGnomeDateEdit): time_t; cdecl; gtk_event_box_set_visible_window: procedure (event_box: PGtkEventBox; visible_window: gboolean); cdecl; gtk_icon_size_lookup_for_settings: function (settings: PGtkSettings; size: TGtkIconSize; width, height: Pgint): gboolean; cdecl; g_filename_display_name: function (const filename: PChar): PChar; cdecl; {$IFDEF KYLIX} gtk_message_dialog_new_with_markup: function (parent:PGtkWindow; flags:TGtkDialogFlags; _type:TGtkMessageType; buttons:TGtkButtonsType; message_format:Pgchar; const format1: PChar; const format2: PChar):PGtkWidget; cdecl; {$ELSE} gtk_message_dialog_new_with_markup: function (parent:PGtkWindow; flags:TGtkDialogFlags; _type:TGtkMessageType; buttons:TGtkButtonsType; message_format:Pgchar):PGtkWidget; varargs; cdecl; {$ENDIF} g_mkdir_with_parents: function (const pathname: PChar; mode: integer): integer; cdecl; procedure LoadGnomeLibs; implementation uses SysUtils, DateUtils, UConfig, UCoreUtils, ULocale, UFileAssoc; (********************************************************************************************************************************) (********************************************************************************************************************************) (********************************************************************************************************************************) (********************************************************************************************************************************) procedure TGnomeColorButton_OnClick(button: PGtkButton; user_data: Pgpointer); cdecl; var Dialog: TGTKColorSelectionDialog; i: integer; begin if Assigned(user_data) then with TGnomeColorButton(user_data) do begin Dialog := TGTKColorSelectionDialog.CreateWithTitle(Parent, LANGSelectFileTypeColor); try Dialog.ShowPalette := True; Dialog.Color := Color; if Byte(Dialog.Run) = 251 then begin for i := 0 to 4 do SetBackgroundColor(i, GDKColorToPGdkColor(Dialog.Color)); FColor := Dialog.Color; if Assigned(FColorChanged) then FColorChanged(TGnomeColorButton(user_data)); end; finally Dialog.Free; end; end; end; procedure TGnomeColorButton_color_set(colorpicker: PGnomeColorPicker; arg1, arg2, arg3, arg4: Word; user_data: Pointer); cdecl; begin if Assigned(user_data) and Assigned(TGnomeColorButton(user_data).FColorChanged) then with TGnomeColorButton(user_data) do begin FColorChanged(TGnomeColorButton(user_data)); FColor := Color; end; end; constructor TGnomeColorButton.Create(AOwner: TComponent); begin inherited Create(AOwner); FUseGnomeUI := Assigned(gnome_color_picker_new) and Assigned(gnome_color_picker_get_i16) and Assigned(gnome_color_picker_set_i16); if FUseGnomeUI then begin FWidget := gnome_color_picker_new; g_signal_connect(PGtkObject(FWidget), 'color-set', G_CALLBACK(@TGnomeColorButton_color_set), Self); end else begin FWidget := gtk_button_new_with_label(''); // This should be here due to height-related troubles g_signal_connect(PGtkObject(FWidget), 'clicked', G_CALLBACK(@TGnomeColorButton_OnClick), Self); BorderStyle := bsHalf; end; Show; FColorChanged := nil; end; destructor TGnomeColorButton.Destroy; begin inherited Destroy; end; function TGnomeColorButton.GetColor: TGDKColor; var R, G, B, A: word; begin if FUseGnomeUI then begin if Enabled then begin gnome_color_picker_get_i16(FWidget, @R, @G, @B, @A); Result := PGdkColorToGDKColor(AllocateColor(nil, R, G, B)); end else Result := FColor; end else Result := FColor; end; procedure TGnomeColorButton.SetColor(Value: TGDKColor); var i: integer; begin FColor := Value; if FUseGnomeUI then gnome_color_picker_set_i16(FWidget, Value.red, Value.green, Value.blue, 0) else for i := 0 to 4 do SetBackgroundColor(i, GDKColorToPGdkColor(Value)); end; procedure TGnomeColorButton.SetDefaultColor; var i: integer; begin if not Enabled then Exit; gtk_widget_set_sensitive(PGtkWidget(FWidget), False); if FUseGnomeUI then begin FColor := GetColor; gnome_color_picker_set_i16(FWidget, 0, 0, 0, 0) // SetColor(PGdkColorToGDKColor(AllocateColor(FWidget, 0, 0, 0){GetDefaultBackgroundColor(1)})); end else for i := 0 to 4 do SetBackgroundColor(i, GetDefaultBackgroundColor(i)); end; procedure TGnomeColorButton.UnsetDefaultColor; var i: integer; begin if Enabled then Exit; gtk_widget_set_sensitive(PGtkWidget(FWidget), True); if FUseGnomeUI then SetColor(FColor) else for i := 0 to 4 do SetBackgroundColor(i, GDKColorToPGdkColor(FColor)); end; (********************************************************************************************************************************) (********************************************************************************************************************************) procedure TGnomeIconEntry_changed(iconentry: PGnomeIconEntry; user_data: gpointer); cdecl; begin if Assigned(user_data) and Assigned(TGnomeIconEntry(user_data).FIconChanged) then TGnomeIconEntry(user_data).FIconChanged(TGnomeIconEntry(user_data)); end; constructor TGnomeIconEntry.Create(AOwner: TComponent); begin inherited Create(AOwner); FIconChanged := nil; FWidget := gnome_icon_entry_new('History', 'Browse'); g_signal_connect(PGtkObject(FWidget), 'changed', G_CALLBACK(@TGnomeIconEntry_changed), Self); Show; end; destructor TGnomeIconEntry.Destroy; begin inherited Destroy; end; function TGnomeIconEntry.GetFilename: string; begin Result := gnome_icon_entry_get_filename(FWidget); end; procedure TGnomeIconEntry.SetFilename(Value: string); begin gnome_icon_entry_set_filename(FWidget, PChar(Value)); end; procedure TGnomeIconEntry.SetPixmapSubdir(Value: string); begin gnome_icon_entry_set_pixmap_subdir(FWidget, PChar(Value)); end; (********************************************************************************************************************************) (********************************************************************************************************************************) constructor TGnomeDateEdit.Create(AOwner: TComponent); begin inherited Create(AOwner); FWidget := gnome_date_edit_new(0, False, True); Show; end; destructor TGnomeDateEdit.Destroy; begin inherited Destroy; end; function TGnomeDateEdit.GetTime: time_t; begin Result := gnome_date_edit_get_time(FWidget); end; procedure TGnomeDateEdit.SetTime(Value: time_t); begin gnome_date_edit_set_time(FWidget, Value); end; (********************************************************************************************************************************) (********************************************************************************************************************************) function button_press_cb(notebook: PGtkWidget; event: PGdkEventButton; data: gpointer): gboolean; cdecl; forward; function button_release_cb(notebook: PGtkWidget; event: PGdkEventButton; data: gpointer): gboolean; cdecl; forward; function scroll_event_callback(widget: PGtkWidget; event: PGdkEventScroll; user_data: gpointer): gboolean; cdecl; forward; function label_ebox_button_pressed(widget: PGtkWidget; event: PGdkEventButton; data: pointer): gboolean; cdecl; forward; constructor TEphyNotebook.Create(AOwner: TComponent); begin inherited Create(AOwner); FTooltips := gtk_tooltips_new; gtk_tooltips_disable(FTooltips); cursor := nil; FShowCloseButtons := False; FShowTooltips := False; FAllowDragDrop := False; FAllowDragOutside := False; FOnNotebookReordered := nil; FOnTabClose := nil; FOnTabDoubleClick := nil; FOnFindNotebookAtPointer := nil; FOnMoveTabToAnotherNotebook := nil; FOnTabFocusOnlyEvent := nil; FBusy := False; // Set up drag-and-drop g_signal_connect(FWidget, 'button-press-event', G_CALLBACK(@button_press_cb), Self); g_signal_connect(FWidget, 'button-release-event', G_CALLBACK(@button_release_cb), Self); g_signal_connect(FWidget, 'scroll-event', G_CALLBACK(@scroll_event_callback), Self); gtk_widget_add_events(FWidget, GDK_BUTTON1_MOTION_MASK); Show; end; destructor TEphyNotebook.Destroy; begin inherited Destroy; end; procedure TEphyNotebook.SetShowCloseButtons(Value: boolean); begin FShowCloseButtons := Value; // Apply settings here end; procedure TEphyNotebook.SetShowTooltips(Value: boolean); begin FShowTooltips := Value; if Value then gtk_tooltips_enable(FTooltips) else gtk_tooltips_disable(FTooltips); end; procedure TEphyNotebook.SetAllowDragDrop(Value: boolean); begin FAllowDragDrop := Value; if (not Value) and drag_in_progress then drag_stop(GDK_CURRENT_TIME (* FIXME? *)); end; procedure TEphyNotebook.SetAllowDragOutside(Value: boolean); begin FAllowDragOutside := Value; if (not Value) and drag_in_progress then drag_stop(GDK_CURRENT_TIME (* FIXME? *)); end; // Following code is inspired by ephy-notebook.c from Epiphany 2.14.1 procedure tab_label_style_set_cb(hbox: PGtkWidget; previous_style: PGtkStyle; user_data: gpointer); cdecl; var button: PGtkWidget; { metrics: PPangoFontMetrics; context: PPangoContext; char_width, }h, w: integer; begin { context := gtk_widget_get_pango_context(hbox); metrics := pango_context_get_metrics(context, hbox^.style^.font_desc, pango_context_get_language(context)); char_width := pango_font_metrics_get_approximate_digit_width(metrics); pango_font_metrics_unref(metrics); } if @gtk_icon_size_lookup_for_settings <> nil then gtk_icon_size_lookup_for_settings(gtk_widget_get_settings(hbox), GTK_ICON_SIZE_MENU, @w, @h) else begin w := 16; h := 16; end; // gtk_widget_set_size_request(hbox, TAB_WIDTH_N_CHARS * PANGO_PIXELS(char_width) + 2 * w, -1); button := g_object_get_data(G_OBJECT(hbox), 'close-button'); gtk_widget_set_size_request(button, w + 2, h + 2); end; procedure close_button_clicked_cb(widget: PGtkWidget; data: pointer); cdecl; var tab: PGtkWidget; ntb: TEphyNotebook; position: integer; CanClose: boolean; begin tab := g_object_get_data(G_OBJECT(widget), 'child'); ntb := TEphyNotebook(data); position := gtk_notebook_page_num(GTK_NOTEBOOK(ntb.FWidget), GTK_WIDGET(tab)); // DebugMsg(['@******* close_button_clicked_cb: Position = ', position, ', data = 0x', IntToHex(Integer(data), 8), ', widget = 0x', IntToHex(Integer(widget), 8)]); CanClose := True; if @ntb.FOnTabClose <> nil then ntb.FOnTabClose(ntb, position, CanClose); if CanClose then gtk_notebook_remove_page(GTK_NOTEBOOK(ntb.FWidget), position); end; function TEphyNotebook.AppendPage(Child: TGTKControl; Caption: string): integer; begin Result := InsertPage(ChildrenCount, Child, Caption); end; function TEphyNotebook.InsertPage(Position: integer; Child: TGTKControl; Caption: string): integer; var hbox, label_hbox, label_ebox, _label, close_button, image, icon: PGtkWidget; rcstyle: PGtkRcStyle; begin // set hbox spacing and label padding (see below) so that there's an equal amount of space around the label hbox := gtk_hbox_new(FALSE, 0); label_ebox := gtk_event_box_new(); if @gtk_event_box_set_visible_window <> nil then gtk_event_box_set_visible_window(GTK_EVENT_BOX (label_ebox), FALSE); gtk_box_pack_start(GTK_BOX (hbox), label_ebox, TRUE, TRUE, 0); g_signal_connect(G_OBJECT(label_ebox), 'button-press-event', G_CALLBACK(@label_ebox_button_pressed), Self); g_object_set_data(G_OBJECT(label_ebox), 'child', Child.FWidget); label_hbox := gtk_hbox_new (FALSE, 0); gtk_container_add(GTK_CONTAINER (label_ebox), label_hbox); // setup close button close_button := gtk_button_new (); gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE); // don't allow focus on the close button g_object_set(G_OBJECT(close_button), 'can-focus', gboolean(FALSE), nil); gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE); rcstyle := gtk_rc_style_new (); rcstyle^.xthickness := Ord(rcstyle^.ythickness = 0); gtk_widget_modify_style (close_button, rcstyle); gtk_rc_style_unref (rcstyle); image := gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU); gtk_container_add (GTK_CONTAINER (close_button), image); gtk_box_pack_start (GTK_BOX (hbox), close_button, FALSE, FALSE, 0); gtk_tooltips_set_tip(FTooltips, close_button, 'Close tab', nil); g_object_set_data(G_OBJECT(close_button), 'tab', hbox); g_object_set_data(G_OBJECT(close_button), 'child', Child.FWidget); g_signal_connect(G_OBJECT(close_button), 'clicked', G_CALLBACK(@close_button_clicked_cb), Self); // setup site icon, empty by default icon := gtk_image_new (); gtk_box_pack_start (GTK_BOX (label_hbox), icon, FALSE, FALSE, 0); // setup label _label := gtk_label_new(PChar(Caption)); // gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END); // gtk_label_set_single_line_mode (GTK_LABEL (label), TRUE); gtk_misc_set_alignment (GTK_MISC(_label), 0.0, 0.5); gtk_misc_set_padding (GTK_MISC(_label), 2, 0); gtk_box_pack_start (GTK_BOX(label_hbox), _label, TRUE, TRUE, 0); // Set minimal size g_signal_connect(hbox, 'style-set', G_CALLBACK(@tab_label_style_set_cb), nil); gtk_widget_show (hbox); gtk_widget_show (label_ebox); gtk_widget_show (label_hbox); gtk_widget_show (_label); gtk_widget_show (image); if FShowCloseButtons then gtk_widget_show (close_button); g_object_set_data (G_OBJECT (hbox), 'label', _label); g_object_set_data (G_OBJECT (hbox), 'label-ebox', label_ebox); g_object_set_data (G_OBJECT (hbox), 'icon', icon); g_object_set_data (G_OBJECT (hbox), 'close-button', close_button); g_object_set_data (G_OBJECT (hbox), 'tooltips', FTooltips); Result := gtk_notebook_insert_page(PGtkNotebook(FWidget), Child.FWidget, hbox, Position); end; procedure TEphyNotebook.RemovePage(PageNo: integer); begin gtk_notebook_remove_page(PGtkNotebook(FWidget), PageNo); end; function TEphyNotebook.GetTabCaption(PageNo: integer): string; var wid, lab: PGtkWidget; begin wid := gtk_notebook_get_tab_label(PGtkNotebook(FWidget), gtk_notebook_get_nth_page(PGtkNotebook(FWidget), PageNo)); lab := g_object_get_data (G_OBJECT(wid), 'label'); Result := PgcharToString(gtk_label_get_text(GTK_LABEL(lab))); end; procedure TEphyNotebook.SetTabCaption(PageNo: integer; Caption: string); var wid, lab: PGtkWidget; begin wid := gtk_notebook_get_tab_label(PGtkNotebook(FWidget), gtk_notebook_get_nth_page(PGtkNotebook(FWidget), PageNo)); lab := g_object_get_data (G_OBJECT(wid), 'label'); gtk_label_set_text(GTK_LABEL(lab), StringToPgchar(Caption)); end; procedure TEphyNotebook.SetTabTooltip(PageNo: integer; Tooltip: string); var wid, lab: PGtkWidget; begin wid := gtk_notebook_get_tab_label(PGtkNotebook(FWidget), gtk_notebook_get_nth_page(PGtkNotebook(FWidget), PageNo)); lab := g_object_get_data (G_OBJECT(wid), 'label-ebox'); gtk_tooltips_set_tip(FTooltips, lab, StringToPgchar(Tooltip), nil); end; function TEphyNotebook.GetPageIndex: integer; begin Result := gtk_notebook_get_current_page(PGtkNotebook(FWidget)); end; procedure TEphyNotebook.SetPageIndex(Value: integer); begin DebugMsg(['SetPageIndex(Value = ', Value, ')']); if (GetPageIndex <> Value) and (Value >= 0) then begin gtk_notebook_set_page(PGtkNotebook(FWidget), Value); if Assigned(FOnTabSwitchedEvent) then FOnTabSwitchedEvent(Self, Value, True); end; end; function TEphyNotebook.find_tab_num_at_pos(abs_x, abs_y: integer): integer; var tab_pos: TGtkPositionType; page_num: integer; page, tab: PGtkWidget; max_x, max_y, x_root, y_root: integer; begin page_num := 0; tab_pos := gtk_notebook_get_tab_pos(PGtkNotebook(FWidget)); if PGtkNotebook(FWidget)^.first_tab = nil then begin DebugMsg(['@***************** first_tab = nil']); Result := AFTER_ALL_TABS; // DebugMsg(['@******* find_tab_num_at_pos(abs_x = ', abs_x, ', abs_y = ', abs_y, ', Result = ', Result, ')']); Exit; end; // For some reason unfullscreen + quick click can cause a wrong click event to be reported to the tab if not is_in_notebook_window(Self, abs_x, abs_y) then begin Result := NOT_IN_APP_WINDOWS; // DebugMsg(['@******* find_tab_num_at_pos(abs_x = ', abs_x, ', abs_y = ', abs_y, ', Result = ', Result, ')']); Exit; end; while gtk_notebook_get_nth_page(PGtkNotebook(FWidget), page_num) <> nil do begin page := gtk_notebook_get_nth_page(PGtkNotebook(FWidget), page_num); tab := gtk_notebook_get_tab_label(PGtkNotebook(FWidget), page); if tab = nil then begin Result := AFTER_ALL_TABS; // DebugMsg(['@******* find_tab_num_at_pos(abs_x = ', abs_x, ', abs_y = ', abs_y, ', Result = ', Result, ')']); Exit; end; if (not GTK_WIDGET_MAPPED(GTK_WIDGET(tab))) then begin Inc(page_num); Continue; end; gdk_window_get_origin(GDK_WINDOW(tab^.window), @x_root, @y_root); max_x := x_root + tab^.allocation.x + tab^.allocation.width; max_y := y_root + tab^.allocation.y + tab^.allocation.height; if (((tab_pos = GTK_POS_TOP) or (tab_pos = GTK_POS_BOTTOM)) and (abs_x <= max_x)) or ((tab_pos = GTK_POS_LEFT) or (tab_pos = GTK_POS_RIGHT)) and (abs_y <= max_y) then begin Result := page_num; // DebugMsg(['@******* find_tab_num_at_pos(abs_x = ', abs_x, ', abs_y = ', abs_y, ', Result = ', Result, ')']); Exit; end; Inc(page_num); end; Result := AFTER_ALL_TABS; // DebugMsg(['@******* find_tab_num_at_pos(abs_x = ', abs_x, ', abs_y = ', abs_y, ', Result = ', Result, ')']); end; function TEphyNotebook.find_notebook_at_pointer(abs_x, abs_y: integer): TEphyNotebook; (* var win_at_pointer{, toplevel_win}: PGdkWindow; x, y: integer; // toplevel: pointer; *) begin Result := nil; if Assigned(FOnFindNotebookAtPointer) then Result := FOnFindNotebookAtPointer(Self, abs_x, abs_y); // toplevel := NULL; { // FIXME multi-head win_at_pointer := gdk_window_at_pointer(@x, @y); if win_at_pointer = nil then begin // We are outside all windows containing a notebook Result := nil; Exit; end; } (* toplevel_win := gdk_window_get_toplevel(win_at_pointer); // get the GtkWidget which owns the toplevel GdkWindow gdk_window_get_user_data(toplevel_win, @toplevel); // toplevel should be an EphyWindow {@@@} if (toplevel <> nil) and GTK_IS_WINDOW toplevel) then begin Result := EPHY_NOTEBOOK(ephy_window_get_notebook(EPHY_WINDOW (toplevel))); Exit; end; *) { Result := nil;} end; function TEphyNotebook.is_in_notebook_window(notebook: TEphyNotebook; abs_x, abs_y: integer): boolean; begin Result := find_notebook_at_pointer(abs_x, abs_y) <> nil; // Result := notebook = find_notebook_at_pointer(abs_x, abs_y); // Result := True; end; (* void terminal_notebook_move_tab (TerminalNotebook *src, TerminalNotebook *dest, TerminalScreen *screen, int dest_position) { if (dest == NULL || src == dest) { gtk_notebook_reorder_child (GTK_NOTEBOOK (src), GTK_WIDGET (screen), dest_position); if (src->priv->drag_in_progress == FALSE) { g_signal_emit (G_OBJECT (src), signals[TABS_REORDERED], 0); } } else { GtkWidget *toplevel; /* make sure the screen isn't destroyed while we move it */ g_object_ref (screen); terminal_notebook_remove_tab (src, screen); /* Set new window for screen so TerminalScreen widget realize function * works. */ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (dest)); g_assert (GTK_WIDGET_TOPLEVEL (toplevel)); g_assert (TERMINAL_IS_WINDOW (toplevel)); terminal_screen_set_window (screen, TERMINAL_WINDOW(toplevel)); terminal_notebook_add_tab (dest, screen, dest_position, TRUE); g_object_unref (screen); } } *) procedure TEphyNotebook.drag_stop(time: guint32); var toplevel{, child}: PGtkWidget; drag_stop_idx: integer; begin DebugMsg(['@######## drag_stop']); toplevel := gtk_widget_get_toplevel(FWidget); if not GTK_WIDGET_TOPLEVEL(toplevel) then begin DebugMsg(['@######## drag_stop: GTK_WIDGET_TOPLEVEL(toplevel) = False']); Exit; end; { child := gtk_bin_get_child(GTK_BIN(toplevel)); if child = nil then begin DebugMsg(['@######## drag_stop: child = nil']); Exit; end; } // disconnect the signals before ungrabbing! if toplevel_grab_broken_handler_id <> 0 then begin g_signal_handler_disconnect(toplevel, toplevel_grab_broken_handler_id); toplevel_grab_broken_handler_id := 0; end; if grab_notify_handler_id <> 0 then begin g_signal_handler_disconnect(FWidget, grab_notify_handler_id); grab_notify_handler_id := 0; end; if toplevel_motion_notify_handler_id <> 0 then begin g_signal_handler_disconnect(toplevel, toplevel_motion_notify_handler_id); toplevel_motion_notify_handler_id := 0; end; if toplevel_button_release_handler_id <> 0 then begin g_signal_handler_disconnect(toplevel, toplevel_button_release_handler_id); toplevel_button_release_handler_id := 0; end; if motion_notify_handler_id <> 0 then begin g_signal_handler_disconnect(FWidget, motion_notify_handler_id); motion_notify_handler_id := 0; end; // ungrab the pointer if it's grabbed // FIXME multihead if gdk_pointer_is_grabbed() then gdk_pointer_ungrab(time); gtk_grab_remove(toplevel); if drag_in_progress then begin if FBusy then Exit; drag_stop_idx := gtk_notebook_get_current_page(GTK_NOTEBOOK(FWidget)); DebugMsg(['@######## drag_stop: final move: ', drag_start_idx, ' --> ', drag_stop_idx]); // The two indexes are equal when moving tab to another notebook if (@FOnNotebookReordered <> nil) and (drag_start_idx <> drag_stop_idx) then begin FBusy := True; FOnNotebookReordered(Self, drag_start_idx, drag_stop_idx); Application.ProcessMessages; FBusy := False; end; end; drag_in_progress := False; x_start := -1; y_start := -1; end; function grab_broken_event_cb(widget: PGtkWidget; event: PGdkEvent; data: gpointer): gboolean; cdecl; begin if data <> nil then TEphyNotebook(data).drag_stop(GDK_CURRENT_TIME (* FIXME? *)); Result := False; end; procedure grab_notify_cb(widget: PGtkWidget; was_grabbed: gboolean; data: gpointer); cdecl; begin if data <> nil then TEphyNotebook(data).drag_stop(GDK_CURRENT_TIME (* FIXME? *)); end; function toplevel_motion_notify_cb(widget: PGtkWidget; event: PGdkEventMotion; data: gpointer): gboolean; cdecl; var ntb, dest: TEphyNotebook; page_num: integer; begin // DebugMsg(['@######## toplevel_motion_notify_cb(..., data = 0x', IntToHex(Integer(data), 8), ')']); Result := False; ntb := TEphyNotebook(data); if ntb.FBusy then Exit; page_num := ntb.find_tab_num_at_pos(Trunc(event^.x_root), Trunc(event^.y_root)); // if page_num >= -1 then ntb.move_tab(page_num); dest := ntb.find_notebook_at_pointer(Trunc(event^.x_root), Trunc(event^.y_root)); // DebugMsg(['@######## toplevel_motion_notify_cb: page_num = ', page_num, ', dest = 0x', IntToHex(Integer(dest), 8), ')']); if (page_num <> NOT_IN_APP_WINDOWS) and (dest <> nil) then begin if (dest <> ntb) and ntb.FAllowDragOutside then ntb.move_tab_to_another_notebook(dest, event) else if page_num >= 0 then ntb.move_tab(page_num) // Same notebook else {DebugMsg(['@######## toplevel_motion_notify_cb: Assertion failed: page_num >= 0'])}; end; (* TerminalNotebook *dest = NULL; int page_num, result; result = find_notebook_and_tab_at_pos ((gint)event->x_root, (gint)event->y_root, &dest, &page_num); if (result != NOT_IN_APP_WINDOWS) { if (dest != notebook) { move_tab_to_another_notebook (notebook, dest, event, page_num); } else { g_assert (page_num >= -1); move_tab (notebook, page_num); } } *) end; function toplevel_button_release_cb(widget: PGtkWidget; event: PGdkEventButton; data: gpointer): gboolean; cdecl; var ntb: TEphyNotebook; // cur_page_num: integer; // cur_page: PGtkWidget; begin DebugMsg(['@######## toplevel_button_release_cb(..., data = 0x', IntToHex(Integer(data), 8), ')']); ntb := TEphyNotebook(data); if ntb.drag_in_progress then begin // cur_page_num := gtk_notebook_get_current_page(GTK_NOTEBOOK(ntb.FWidget)); // cur_page := gtk_notebook_get_nth_page(GTK_NOTEBOOK(ntb.FWidget), cur_page_num); (* if (!is_in_notebook_window (notebook, event->x_root, event->y_root) && gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)) > 1) { /* Tab was detached */ g_signal_emit (G_OBJECT (notebook), signals[TAB_DETACHED], 0, cur_page); } *) end; // This must be called even if a drag isn't happening ntb.drag_stop(event^.time); Result := False; end; function TEphyNotebook.drag_start(time: guint32): gboolean; var toplevel, child: PGtkWidget; begin DebugMsg(['@######## drag_start']); Result := False; // FIXME multihead if drag_in_progress or gdk_pointer_is_grabbed() then Exit; if not FAllowDragDrop then Exit; if FBusy then Exit; drag_in_progress := True; // get a new cursor, if necessary // FIXME multi-head if cursor = nil then cursor := gdk_cursor_new(GDK_FLEUR); toplevel := gtk_widget_get_toplevel(FWidget); if not GTK_WIDGET_TOPLEVEL(toplevel) then begin DebugMsg(['@######## drag_start: GTK_WIDGET_TOPLEVEL(toplevel) = False']); Result := False; Exit; end; child := gtk_bin_get_child(GTK_BIN(toplevel)); if child = nil then begin DebugMsg(['@######## drag_start: child = nil']); Result := False; Exit; end; // grab the pointer gtk_grab_add(toplevel); // FIXME multi-head if gdk_pointer_grab(toplevel^.window, False, GDK_BUTTON1_MOTION_MASK or GDK_BUTTON_RELEASE_MASK, nil, cursor, time) <> GDK_GRAB_SUCCESS then begin DebugMsg(['@######## drag_start: gdk_pointer_grab <> GDK_GRAB_SUCCESS']); drag_stop(time); Result := False; Exit; end; if (toplevel_grab_broken_handler_id = 0) and (toplevel_motion_notify_handler_id = 0) and (toplevel_button_release_handler_id = 0) and (grab_notify_handler_id = 0) then begin toplevel_grab_broken_handler_id := g_signal_connect(toplevel, 'grab-broken-event', G_CALLBACK(@grab_broken_event_cb), Self); toplevel_motion_notify_handler_id := g_signal_connect(toplevel, 'motion-notify-event', G_CALLBACK(@toplevel_motion_notify_cb), Self); toplevel_button_release_handler_id := g_signal_connect(toplevel, 'button-release-event', G_CALLBACK(@toplevel_button_release_cb), Self); grab_notify_handler_id := g_signal_connect(FWidget, 'grab-notify', G_CALLBACK(@grab_notify_cb), Self); drag_start_idx := gtk_notebook_get_current_page(GTK_NOTEBOOK(FWidget)); Result := True; end else begin DebugMsg(['@######## drag_start: some handlers are non-null']); Result := False; end; end; // this function is only called during dnd, we don't need to emit TABS_REORDERED here, instead we do it on drag_stop procedure TEphyNotebook.move_tab(dest_position: integer); var cur_page_num: integer; cur_tab: PGtkWidget; begin cur_page_num := gtk_notebook_get_current_page(GTK_NOTEBOOK(FWidget)); // DebugMsg(['@######## move_tab(dest_position = ', dest_position, ', cur_page_num = ', cur_page_num]); if (dest_position <> cur_page_num) and (dest_position >= 0) then begin cur_tab := gtk_notebook_get_nth_page(GTK_NOTEBOOK(FWidget), cur_page_num); gtk_notebook_reorder_child(GTK_NOTEBOOK(FWidget), cur_tab, dest_position); { terminal_notebook_move_tab (TERMINAL_NOTEBOOK (notebook), NULL, TERMINAL_SCREEN (cur_tab), dest_position); } end; end; function motion_notify_cb(notebook: PGtkWidget; event: PGdkEventMotion; data: gpointer): gboolean; cdecl; var ntb: TEphyNotebook; begin // DebugMsg(['@######## motion_notify_cb(..., data = 0x', IntToHex(Integer(data), 8), ')']); ntb := TEphyNotebook(data); Result := False; if (not ntb.drag_in_progress) and (ntb.x_start >= 0) and (ntb.y_start >= 0) and gtk_drag_check_threshold(GTK_WIDGET(notebook), ntb.x_start, ntb.y_start, Trunc(event^.x_root), Trunc(event^.y_root)) then Result := ntb.drag_start(event^.time); end; procedure TEphyNotebook.move_tab_to_another_notebook(dest: TEphyNotebook; event: PGdkEventMotion); var cur_page, dest_page_num: integer; begin // This is getting tricky, the screen was dragged in a notebook in another window of the same app, we move the screen // to that new notebook, and let this notebook handle the drag DebugMsg(['@######## move_tab_to_another_notebook(dest = 0x', IntToHex(Integer(pointer(dest)), 8), ')']); if not (dest is TEphyNotebook) then begin DebugMsg(['@######## move_tab_to_another_notebook: dest(', dest.ClassName, ') is not TEphyNotebook']); Exit; end; if Self = dest then begin DebugMsg(['@######## move_tab_to_another_notebook: dest = Self']); Exit; end; if not Assigned(@FOnMoveTabToAnotherNotebook) then begin DebugMsg(['@######## move_tab_to_another_notebook: FOnMoveTabToAnotherNotebook not handled, giving up.']); Exit; end; cur_page := gtk_notebook_get_current_page(GTK_NOTEBOOK(FWidget)); // stop drag in origin window // ungrab the pointer if it's grabbed drag_stop(event^.time); { if gdk_pointer_is_grabbed() then gdk_pointer_ungrab(event^.time); gtk_grab_remove(FWidget); } (* g_assert (TERMINAL_IS_NOTEBOOK (dest)); g_assert (dest != src); cur_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (src)); screen = gtk_notebook_get_nth_page (GTK_NOTEBOOK (src), cur_page); /* stop drag in origin window */ /* ungrab the pointer if it's grabbed */ drag_stop (src, event->time); if (gdk_pointer_is_grabbed ()) { gdk_pointer_ungrab (event->time); } gtk_grab_remove (GTK_WIDGET (src)); terminal_notebook_move_tab (src, dest, TERMINAL_SCREEN (screen), dest_position); /* start drag handling in dest notebook */ dest->priv->motion_notify_handler_id = g_signal_connect (G_OBJECT (dest), "motion-notify-event", G_CALLBACK (motion_notify_cb), NULL); *) dest_page_num := dest.find_tab_num_at_pos(Trunc(event^.x_root), Trunc(event^.y_root)); if FOnMoveTabToAnotherNotebook(Self, dest, cur_page, dest_page_num) then begin // move newly created tab in dest notebook to position of mouse cursor // toplevel_motion_notify_cb(dest.FWidget, event, dest); Application.ProcessMessages; // start drag handling in dest notebook dest.x_start := Trunc(event^.x_root); dest.y_start := Trunc(event^.y_root); dest.motion_notify_handler_id := g_signal_connect(G_OBJECT(dest.FWidget), 'motion-notify-event', G_CALLBACK(@motion_notify_cb), dest); dest.drag_start(event^.time); { dest.motion_notify_handler_id := g_signal_connect(G_OBJECT(dest.FWidget), 'motion-notify-event', G_CALLBACK(@motion_notify_cb), nil); dest.drag_start(event^.time); } end; end; function button_release_cb(notebook: PGtkWidget; event: PGdkEventButton; data: gpointer): gboolean; cdecl; var ntb: TEphyNotebook; begin ntb := TEphyNotebook(data); DebugMsg(['@######## button_release_cb(..., data = 0x', IntToHex(Integer(data), 8), ')']); // This must be called even if a drag isn't happening ntb.drag_stop(event^.time); Result := False; end; function label_ebox_button_pressed(widget: PGtkWidget; event: PGdkEventButton; data: pointer): gboolean; cdecl; var tab: PGtkWidget; ntb: TEphyNotebook; position: integer; begin Result := False; ntb := TEphyNotebook(data); tab := g_object_get_data(G_OBJECT(widget), 'child'); position := gtk_notebook_page_num(GTK_NOTEBOOK(ntb.FWidget), GTK_WIDGET(tab)); // DebugMsg(['@******* label_ebox_button_pressed, button = ', event^.button, ', Position = ', position, ', data = 0x', IntToHex(Integer(data), 8), ', widget = 0x', IntToHex(Integer(widget), 8)]); if (((event^.button = 2) and (event^._type = GDK_BUTTON_PRESS)) or // Middle-click ((event^.button = 1) and (event^._type = GDK_2BUTTON_PRESS))) and // Double-click Assigned(ntb.FOnTabDoubleClick) then begin ntb.FOnTabDoubleClick(ntb, position); Result := True; ntb.x_start := -1; ntb.y_start := -1; end; end; function button_press_cb(notebook: PGtkWidget; event: PGdkEventButton; data: gpointer): gboolean; cdecl; var tab_clicked: integer; ntb: TEphyNotebook; begin Result := False; ntb := TEphyNotebook(data); if ntb.FBusy then Exit; DebugMsg(['@######## button_press_cb(..., data = 0x', IntToHex(Integer(data), 8), ')']); tab_clicked := ntb.find_tab_num_at_pos(Trunc(event^.x_root), Trunc(event^.y_root)); DebugMsg(['@################# tab_clicked = ', tab_clicked]); if ntb.drag_in_progress then begin DebugMsg(['drag_in_progress, exiting.']); Result := True; Exit; end; if ((event^.button = 1) or (event^.button = 3)) and (event^._type = GDK_BUTTON_PRESS) and (tab_clicked >= 0) then begin if event^.button = 1 then begin ntb.x_start := Trunc(event^.x_root); ntb.y_start := Trunc(event^.y_root); DebugMsg(['@################# x_start = ', ntb.x_start, ', y_start = ', ntb.y_start]); ntb.motion_notify_handler_id := g_signal_connect(G_OBJECT(notebook), 'motion-notify-event', G_CALLBACK(@motion_notify_cb), data); end; if tab_clicked >= 0 then begin if tab_clicked <> gtk_notebook_get_current_page(PGtkNotebook(ntb.FWidget)) then begin ntb.FBusy := True; gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), tab_clicked); if Assigned(ntb.FOnTabSwitchedEvent) then ntb.FOnTabSwitchedEvent(ntb, tab_clicked, True); Application.ProcessMessages; ntb.FBusy := False; end else begin if Assigned(ntb.FOnTabFocusOnlyEvent) then ntb.FOnTabFocusOnlyEvent(ntb, tab_clicked); end; end; // don't consume the event if right mouse button pressed - allow display the popup menu Result := event^.button <> 3; end else Result := True; // consume the event if not clicked any tab { else if (GDK_BUTTON_PRESS = event^._type) and (event^.button = 3) then begin if tab_clicked = -1 then begin // consume event, so that we don't pop up the context menu when the mouse if not over a screen label Result := True; end else // switch to the page the mouse is over, but don't consume the event if (tab_clicked >= 0) then gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), tab_clicked); end; } end; function scroll_event_callback(widget: PGtkWidget; event: PGdkEventScroll; user_data: gpointer): gboolean; cdecl; var ntb: TEphyNotebook; page: integer; child: PGtkWidget; px, py: gint; begin Result := True; ntb := TEphyNotebook(user_data); if ntb.FBusy then Exit; child := gtk_notebook_get_nth_page(GTK_NOTEBOOK(ntb.FWidget), gtk_notebook_get_current_page(PGtkNotebook(ntb.FWidget))); if (child <> nil) then begin // DebugMsg(['scroll_event_callback: x = ', event^.x, ', y = ', event^.y]); px := 0; py := 0; gtk_widget_get_pointer(child, @px, @py); // DebugMsg(['scroll_event_callback: px = ', px, ', py = ', py]); if (px >= 0) and (py >= 0) then Exit; end; page := gtk_notebook_get_current_page(PGtkNotebook(ntb.FWidget)) - Ord(event^.direction = GDK_SCROLL_UP) + Ord(event^.direction = GDK_SCROLL_DOWN); if (page < 0) or (page > ntb.ChildrenCount - 1) then Exit; ntb.FBusy := True; gtk_notebook_set_current_page(PGtkNotebook(ntb.FWidget), page); //* FIXME: should this be ShouldFocus = False? if Assigned(ntb.FOnTabSwitchedEvent) then ntb.FOnTabSwitchedEvent(ntb, page, True); Application.ProcessMessages; ntb.FBusy := False; end; (********************************************************************************************************************************) (********************************************************************************************************************************) function Gnome_MessageBox_key_press_event(widget: PGtkWidget; event: PGdkEventKey; user_data : gpointer): gboolean; cdecl; begin Result := False; if event^.keyval = GDK_ESCAPE then begin gtk_dialog_response(PGtkDialog(widget), integer(user_data)); // Beep; Result := True; end; end; function MessageBoxShowOnce(ParentWindow: PGtkWindow; const Text: string; const DontShowAgainText: string; var DontShowAgainChecked: boolean; Buttons: TMessageButtons = [mbOK]; Style: TMessageStyle = mbInfo; Default: TMessageButton = mbNone; Escape: TMessageButton = mbNone): TMessageButton; const TMessageStyleID : array[0..3] of TGtkMessageType = (GTK_MESSAGE_ERROR, GTK_MESSAGE_INFO, GTK_MESSAGE_QUESTION, GTK_MESSAGE_WARNING); var Dialog: PGtkWidget; CheckBox: PGtkCheckButton; i: integer; w: PGtkWidget; begin if Application.Terminated then begin Result := Escape; Exit; end; Dialog := gtk_message_dialog_new(ParentWindow, GTK_DIALOG_MODAL or GTK_DIALOG_DESTROY_WITH_PARENT, TMessageStyleID[Integer(Style)], GTK_BUTTONS_NONE, StringToPgchar(Text)); CheckBox := PGtkCheckButton(gtk_check_button_new_with_mnemonic(StringToPgchar(DontShowAgainText))); gtk_widget_show(PGtkWidget(CheckBox)); gtk_box_pack_end(GTK_BOX(GTK_DIALOG(Dialog).vbox), PGtkWidget(CheckBox), False, False, 12); for i := 1 to NumMessageButtons do if TMessageButton(i - 1) in Buttons then begin w := gtk_dialog_add_button(PGtkDialog(Dialog), MessageButtonID[i], i); if TMessageButton(i - 1) = Default then gtk_widget_grab_focus(w); end; if Escape <> mbNone then g_signal_connect(PGtkObject(Dialog), 'key-press-event', G_CALLBACK(@Gnome_MessageBox_key_press_event), Pointer(Ord(Escape) + 1{MessageButtonID[Ord(Escape)]})); if Default <> mbNone then gtk_dialog_set_default_response(PGtkDialog(Dialog), Ord(Default)); Result := TMessageButton(gtk_dialog_run(PGtkDialog(Dialog)) - 1); DontShowAgainChecked := gtk_toggle_button_get_active(PGtkToggleButton(CheckBox)); gtk_widget_destroy(Dialog); end; (********************************************************************************************************************************) (********************************************************************************************************************************) // The following code has been stolen from gtkmountoperation.c // Copyright (C) Christian Kellner procedure HandleVFSAskQuestionCallback(DialogParent: PGtkWidget; const AMessage: PChar; const Choices: PPChar; choice: PInteger); var dialog: PGtkWidget; secondary: PChar; primary: PChar; count, len, res: integer; begin len := 0; secondary := nil; if (AMessage = nil) or (choices = nil) or Application.Terminated then Exit; primary := strstr (AMessage, #10); if primary <> nil then begin secondary := primary + 1; primary := g_strndup (AMessage, primary - AMessage); end; if (primary <> nil) and (secondary <> nil) and (@gtk_message_dialog_new_with_markup <> nil) then dialog := gtk_message_dialog_new_with_markup (PGtkWindow(DialogParent), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, '%s'#10'%s', primary, secondary) else dialog := gtk_message_dialog_new (PGtkWindow(DialogParent), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, '%s', AMessage); if primary <> nil then g_free (primary); // gtk_window_set_title (PGtkWindow(dialog), 'VFS Question'); gtk_window_set_title (PGtkWindow(dialog), ''); // First count the items in the list then add the buttons in reverse order while (TOpenPCharArray(choices)[len] <> nil) do Inc(len); for count := len - 1 downto 0 do gtk_dialog_add_button (PGtkDialog(dialog), TOpenPCharArray(choices)[count], count); res := gtk_dialog_run (PGtkDialog(Dialog)); if choice <> nil then choice^ := res; gtk_widget_destroy (PGtkWidget(Dialog)); end; (********************************************************************************************************************************) type PVFSAskPasswordCallbackPriv = ^TVFSAskPasswordCallbackPriv; TVFSAskPasswordCallbackPriv = record dialog: PGtkDialog; entry_container: PGtkWidget; choice_anon, choice_user: PGtkWidget; username_entry, domain_entry, password_entry: PGtkEntry; end; function entry_has_input(entry_widget: PGtkEntry): boolean; var text: PChar; begin if entry_widget = nil then Result := True else begin text := gtk_entry_get_text (entry_widget); Result := (text <> nil) and (strlen(text) > 0); end; end; function pw_dialog_input_is_valid(priv: PVFSAskPasswordCallbackPriv): boolean; begin Result := entry_has_input(priv^.username_entry) and entry_has_input(priv^.domain_entry) and entry_has_input(priv^.password_entry); end; procedure pw_dialog_verify_input(editable: PGtkEditable; priv: PVFSAskPasswordCallbackPriv); cdecl; begin gtk_dialog_set_response_sensitive (GTK_DIALOG (priv^.dialog), GTK_RESPONSE_OK, pw_dialog_input_is_valid(priv)); end; procedure pw_dialog_anonymous_toggled(widget: PGtkWidget; priv: PVFSAskPasswordCallbackPriv); cdecl; var is_valid, anonymous: boolean; begin anonymous := widget = priv^.choice_anon; if anonymous then is_valid := True else is_valid := pw_dialog_input_is_valid(priv); gtk_widget_set_sensitive (priv^.entry_container, not anonymous); gtk_dialog_set_response_sensitive(GTK_DIALOG (priv^.dialog), GTK_RESPONSE_OK, is_valid); end; procedure pw_dialog_cycle_focus(widget: PGtkWidget; priv: PVFSAskPasswordCallbackPriv); cdecl; var next_widget: PGtkWidget; begin next_widget := nil; if widget = PGtkWidget(priv^.username_entry) then begin if priv^.domain_entry <> nil then next_widget := PGtkWidget(priv^.domain_entry) else if priv^.password_entry <> nil then next_widget := PGtkWidget(priv^.password_entry); end else if (widget = PGtkWidget(priv^.domain_entry)) and (priv^.password_entry <> nil) then next_widget := PGtkWidget(priv^.password_entry); if next_widget <> nil then gtk_widget_grab_focus(next_widget) else if pw_dialog_input_is_valid(priv) then gtk_window_activate_default(GTK_WINDOW(priv^.dialog)); end; procedure unmask_checkbox_toggled(togglebutton: PGtkToggleButton; priv: PVFSAskPasswordCallbackPriv); cdecl; var active: gboolean; begin active := gtk_toggle_button_get_active(togglebutton); if priv^.password_entry <> nil then gtk_entry_set_visibility(priv^.password_entry, active); end; // The following code has been stolen from gtkmountoperation.c // Copyright (C) Christian Kellner function HandleVFSAskPasswordCallback(DialogParent: PGtkWidget; const AMessage: PChar; const default_user: PChar; const default_domain: PChar; const default_password: PChar; flags: TVFSAskPasswordFlags; username: PPChar; password: PPChar; anonymous: PInteger; domain: PPChar; password_save: PVFSPasswordSave): LongBool; function table_add_entry(table: PGtkWidget; row: integer; const label_text: PChar; const value: PChar; user_data: PVFSAskPasswordCallbackPriv): PGtkEntry; var entry: PGtkEntry; alabel: PGtkWidget; begin alabel := gtk_label_new_with_mnemonic (label_text); gtk_misc_set_alignment (GTK_MISC (alabel), 0.0, 0.5); entry := PGtkEntry(gtk_entry_new ()); if value <> nil then gtk_entry_set_text (PGtkEntry(entry), value); gtk_table_attach (GTK_TABLE (table), alabel, 0, 1, row, row + 1, GTK_FILL, GTK_EXPAND or GTK_FILL, 0, 0); gtk_table_attach_defaults (GTK_TABLE (table), PGtkWidget(entry), 1, 2, row, row + 1); gtk_label_set_mnemonic_widget (PGtkLabel(alabel), PGtkWidget(entry)); g_signal_connect (entry, 'changed', G_CALLBACK (@pw_dialog_verify_input), user_data); g_signal_connect (entry, 'activate', G_CALLBACK (@pw_dialog_cycle_focus), user_data); Result := entry; end; var widget: PGtkWidget; window: PGtkWindow; hbox, main_vbox, vbox, icon: PGtkWidget; table: PGtkWidget; message_label: PGtkWidget; can_anonymous: boolean; Rows: integer; anon_box: PGtkWidget; radio_forget, radio_session, radio_remember: PGtkWidget; group: PGSList; remember_box: PGtkWidget; priv: TVFSAskPasswordCallbackPriv; s: PChar; unmask_checkbox: PGtkWidget; begin FillChar(priv, sizeof(priv), 0); radio_forget := nil; radio_session := nil; radio_remember := nil; widget := gtk_dialog_new (); priv.dialog := PGtkDialog(widget); window := PGtkWindow(widget); // gtk_window_set_title (window, 'VFS Question'); gtk_window_set_title (window, ''); // Set the dialog up with HIG properties gtk_dialog_set_has_separator (priv.dialog, False); gtk_container_set_border_width (GTK_CONTAINER (priv.dialog), 5); gtk_box_set_spacing (GTK_BOX (priv.dialog^.vbox), 2); // 2 * 5 + 2 = 12 gtk_container_set_border_width (GTK_CONTAINER (priv.dialog^.action_area), 5); gtk_box_set_spacing (GTK_BOX (priv.dialog^.action_area), 6); gtk_window_set_resizable (window, False); // gtk_window_set_icon_name (window, 'gtk-dialog-authentication'); if (flags and VFS_ASK_PASSWORD_ARCHIVE_MODE) = VFS_ASK_PASSWORD_ARCHIVE_MODE then s := GTK_STOCK_OK else s := PChar(LANGConnMgr_ConnectButton); gtk_dialog_add_buttons (priv.dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, s, GTK_RESPONSE_OK, nil); gtk_dialog_set_default_response (priv.dialog, GTK_RESPONSE_OK); // Build contents hbox := gtk_hbox_new (FALSE, 12); gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); gtk_box_pack_start (GTK_BOX (priv.dialog^.vbox), hbox, TRUE, TRUE, 0); if gtk_style_lookup_icon_set(gtk_widget_get_style(PGtkWidget(priv.dialog)), 'gtk-dialog-authentication') <> nil then icon := gtk_image_new_from_stock('gtk-dialog-authentication', GTK_ICON_SIZE_DIALOG) else icon := gtk_image_new_from_pixbuf(StockLock48.FPixbuf); gtk_misc_set_alignment (GTK_MISC (icon), 0.5, 0.0); gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0); main_vbox := gtk_vbox_new (FALSE, 18); gtk_box_pack_start (GTK_BOX (hbox), main_vbox, TRUE, TRUE, 0); message_label := gtk_label_new (AMessage); gtk_misc_set_alignment (GTK_MISC (message_label), 0.0, 0.5); gtk_label_set_line_wrap (GTK_LABEL (message_label), TRUE); gtk_box_pack_start (GTK_BOX (main_vbox), GTK_WIDGET (message_label), FALSE, FALSE, 0); vbox := gtk_vbox_new (FALSE, 6); gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0); can_anonymous := (flags and VFS_ASK_PASSWORD_ANONYMOUS_SUPPORTED) = VFS_ASK_PASSWORD_ANONYMOUS_SUPPORTED; if can_anonymous then begin anon_box := gtk_vbox_new (FALSE, 6); gtk_box_pack_start (GTK_BOX (vbox), anon_box, FALSE, FALSE, 0); priv.choice_anon := gtk_radio_button_new_with_mnemonic (nil, PChar(LANGGtkMountOperation_ConnectAnonymously)); gtk_box_pack_start (GTK_BOX (anon_box), priv.choice_anon, FALSE, FALSE, 0); g_signal_connect (priv.choice_anon, 'toggled', G_CALLBACK (@pw_dialog_anonymous_toggled), @priv); group := gtk_radio_button_get_group (GTK_RADIO_BUTTON (priv.choice_anon)); priv.choice_user := gtk_radio_button_new_with_mnemonic (group, PChar(LANGGtkMountOperation_ConnectAsUser)); gtk_box_pack_start (GTK_BOX (anon_box), priv.choice_user, FALSE, FALSE, 0); g_signal_connect (priv.choice_user, 'toggled', G_CALLBACK (@pw_dialog_anonymous_toggled), @priv); end; rows := 0; if (flags and VFS_ASK_PASSWORD_NEED_PASSWORD) = VFS_ASK_PASSWORD_NEED_PASSWORD then Inc(rows); if (flags and VFS_ASK_PASSWORD_NEED_USERNAME) = VFS_ASK_PASSWORD_NEED_USERNAME then Inc(rows); if (flags and VFS_ASK_PASSWORD_NEED_DOMAIN) = VFS_ASK_PASSWORD_NEED_DOMAIN then Inc(rows); // The table that holds the entries priv.entry_container := gtk_alignment_new (0.0, 0.0, 1.0, 1.0); if can_anonymous and Application.GTKVersion_2_4_0_Up then g_object_set (priv.entry_container, 'left-padding', 12, nil); gtk_box_pack_start (GTK_BOX (vbox), priv.entry_container, FALSE, FALSE, 0); table := gtk_table_new (rows, 2, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 12); gtk_table_set_row_spacings (GTK_TABLE (table), 6); gtk_container_add (GTK_CONTAINER (priv.entry_container), table); Rows := 0; if (flags and VFS_ASK_PASSWORD_NEED_USERNAME) = VFS_ASK_PASSWORD_NEED_USERNAME then begin priv.username_entry := table_add_entry (table, rows, PChar(LANGGtkMountOperation_Username), default_user, @priv); Inc(Rows); end; if (flags and VFS_ASK_PASSWORD_NEED_DOMAIN) = VFS_ASK_PASSWORD_NEED_DOMAIN then begin priv.domain_entry := table_add_entry (table, rows, PChar(LANGGtkMountOperation_Domain), default_domain, @priv); Inc(Rows); end; if (flags and VFS_ASK_PASSWORD_NEED_PASSWORD) = VFS_ASK_PASSWORD_NEED_PASSWORD then begin priv.password_entry := table_add_entry (table, rows, PChar(LANGGtkMountOperation_Password), nil, @priv); if (default_password <> nil) and (strlen(default_password) > 0) then gtk_entry_set_text(priv.password_entry, default_password); // Inc(Rows); gtk_entry_set_visibility (GTK_ENTRY (priv.password_entry), FALSE); end; if (flags and VFS_ASK_PASSWORD_ARCHIVE_MODE) = VFS_ASK_PASSWORD_ARCHIVE_MODE then begin unmask_checkbox := gtk_check_button_new_with_mnemonic (PChar(LANGFSetPassword_ShowPasswordCheckButton)); g_object_set(G_OBJECT(unmask_checkbox), 'can-focus', 0, nil); gtk_box_pack_start (GTK_BOX (vbox), unmask_checkbox, FALSE, FALSE, 0); g_signal_connect (unmask_checkbox, 'toggled', G_CALLBACK (@unmask_checkbox_toggled), @priv); end; if ((flags and VFS_ASK_PASSWORD_SAVING_SUPPORTED) = VFS_ASK_PASSWORD_SAVING_SUPPORTED) or ((flags and VFS_ASK_PASSWORD_SAVE_INTERNAL) = VFS_ASK_PASSWORD_SAVE_INTERNAL) then begin remember_box := gtk_vbox_new (FALSE, 6); gtk_box_pack_start (GTK_BOX (vbox), remember_box, FALSE, FALSE, 0); if (flags and VFS_ASK_PASSWORD_SAVING_SUPPORTED) = 0 then s := PChar(LANGGtkMountOperation_DoNotSavePassword) else s := PChar(LANGGtkMountOperation_ForgetPasswordImmediately); radio_forget := gtk_radio_button_new_with_mnemonic (nil, s); gtk_box_pack_start (GTK_BOX (remember_box), radio_forget, FALSE, FALSE, 0); group := gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_forget)); // gnome-keyring only if (flags and VFS_ASK_PASSWORD_SAVING_SUPPORTED) = VFS_ASK_PASSWORD_SAVING_SUPPORTED then begin radio_session := gtk_radio_button_new_with_mnemonic (group, PChar(LANGGtkMountOperation_RememberPasswordUntilYouLogout)); gtk_box_pack_start (GTK_BOX (remember_box), radio_session, FALSE, FALSE, 0); group := gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_session)); end; if (flags and VFS_ASK_PASSWORD_SAVING_SUPPORTED) = 0 then s := PChar(LANGGtkMountOperation_SavePasswordInConnectionManager) else s := PChar(LANGGtkMountOperation_RememberForever); radio_remember := gtk_radio_button_new_with_mnemonic (group, s); gtk_box_pack_start (GTK_BOX (remember_box), radio_remember, FALSE, FALSE, 0); // Select to save password when internal saving is supported if (flags and VFS_ASK_PASSWORD_SAVE_INTERNAL) = VFS_ASK_PASSWORD_SAVE_INTERNAL then gtk_toggle_button_set_active(PGtkToggleButton(radio_remember), True); end; // The anonymous option will be active by default, ensure the toggled signal is emitted for it. if can_anonymous then gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (priv.choice_anon)) else if not pw_dialog_input_is_valid(@priv) then gtk_dialog_set_response_sensitive(priv.dialog, GTK_RESPONSE_OK, False); gtk_widget_show_all (GTK_WIDGET (priv.dialog)); gtk_window_set_transient_for (window, PGtkWindow(DialogParent)); Result := gtk_dialog_run(PGtkDialog(priv.dialog)) = GTK_RESPONSE_OK; if Result then begin if (priv.username_entry <> nil) and (username <> nil) then username^ := g_strdup(gtk_entry_get_text(priv.username_entry)); if (priv.domain_entry <> nil) and (domain <> nil) then domain^ := g_strdup(gtk_entry_get_text(priv.domain_entry)); if (priv.password_entry <> nil) and (password <> nil) then password^ := g_strdup(gtk_entry_get_text(priv.password_entry)); if (priv.choice_anon <> nil) and (anonymous <> nil) then anonymous^ := Ord(gtk_toggle_button_get_active(PGtkToggleButton(priv.choice_anon))); if (password_save <> nil) and (radio_forget <> nil) and (radio_remember <> nil) then begin if gtk_toggle_button_get_active(PGtkToggleButton(radio_forget)) then password_save^ := VFS_PASSWORD_SAVE_NEVER else if (radio_session <> nil) and gtk_toggle_button_get_active(PGtkToggleButton(radio_session)) then password_save^ := VFS_PASSWORD_SAVE_FOR_SESSION else if gtk_toggle_button_get_active(PGtkToggleButton(radio_remember)) then password_save^ := VFS_PASSWORD_SAVE_PERMANENTLY; end; end; gtk_widget_destroy (PGtkWidget(priv.dialog)); end; (********************************************************************************************************************************) (********************************************************************************************************************************) (********************************************************************************************************************************) (********************************************************************************************************************************) (********************************************************************************************************************************) (********************************************************************************************************************************) (********************************************************************************************************************************) (********************************************************************************************************************************) procedure SetupGnomeLibs; var g: Pointer; begin try if Assigned(gnome_program_init) and Assigned(libgnomeui_module_info_get) and Assigned(libgnome_module_info_get) then begin DebugMsg(['Initializing Gnome...']); g := gnome_program_init('TuxCommander', PChar(ConstAboutVersion), libgnomeui_module_info_get, GTKForms.argc, GTKForms.argv, 'show-crash-dialog', 0, nil); DebugMsg([' *GnomeProgram = 0x', IntToHex(QWORD(g), 8)]); end; except end; end; procedure LoadGnomeLibs; begin // Set default values @gnome_about_new := nil; @gnome_color_picker_new := nil; @gnome_color_picker_get_i16 := nil; @gnome_color_picker_set_i16 := nil; @libgnomeui_module_info_get := nil; @gnome_program_init := nil; @libgnome_module_info_get := nil; @gnome_icon_entry_new := nil; @gnome_icon_entry_set_pixmap_subdir := nil; @gnome_icon_entry_get_filename := nil; @gnome_icon_entry_set_filename := nil; @gtk_event_box_set_visible_window := nil; // Dynamic loading libGlib2Handle := dlopen('libglib-2.0.so.0', RTLD_LAZY); if libGlib2Handle = nil then libGlib2Handle := dlopen('libglib-2.0.so', RTLD_LAZY); if libGlib2Handle <> nil then begin @g_filename_display_name := dlsym(libGlib2Handle, 'g_filename_display_name'); @g_mkdir_with_parents := dlsym(libGlib2Handle, 'g_mkdir_with_parents'); DebugMsg(['libglib-2.0.so loaded, @g_filename_display_name = 0x', IntToHex(QWORD(@g_filename_display_name), 8)]); end; libGtk2Handle := dlopen('libgtk-x11-2.0.so.0', RTLD_LAZY); if libGtk2Handle = nil then libGtk2Handle := dlopen('libgtk-x11-2.0.so', RTLD_LAZY); if libGtk2Handle <> nil then begin @gtk_event_box_set_visible_window := dlsym(libGtk2Handle, 'gtk_event_box_set_visible_window'); @gtk_icon_size_lookup_for_settings := dlsym(libGtk2Handle, 'gtk_icon_size_lookup_for_settings'); @gtk_message_dialog_new_with_markup := dlsym(libGtk2Handle, 'gtk_message_dialog_new_with_markup'); DebugMsg(['libgtk-x11-2.0.so loaded, @gtk_event_box_set_visible_window = 0x', IntToHex(QWORD(@gtk_event_box_set_visible_window), 8), ', @gtk_icon_size_lookup_for_settings = 0x', IntToHex(QWORD(@gtk_icon_size_lookup_for_settings), 8)]); end; libGnome2Handle := dlopen('libgnome-2.so.0', RTLD_LAZY); if libGnome2Handle = nil then libGnome2Handle := dlopen('libgnome-2.so', RTLD_LAZY); if libGnome2Handle <> nil then begin @gnome_program_init := dlsym(libGnome2Handle, 'gnome_program_init'); @libgnome_module_info_get := dlsym(libGnome2Handle, 'libgnome_module_info_get'); DebugMsg(['libgnome-2.so loaded, @gnome_program_init = 0x', IntToHex(QWORD(@gnome_program_init), 8), ', @libgnome_module_info_get = 0x', IntToHex(QWORD(@libgnome_module_info_get), 8)]); end; libGnomeUI2Handle := dlopen('libgnomeui-2.so.0', RTLD_LAZY); if libGnomeUI2Handle = nil then libGnomeUI2Handle := dlopen('libgnomeui-2.so', RTLD_LAZY); if libGnomeUI2Handle <> nil then begin @gnome_about_new := dlsym(libGnomeUI2Handle, 'gnome_about_new'); @gnome_color_picker_new := dlsym(libGnomeUI2Handle, 'gnome_color_picker_new'); @gnome_color_picker_get_i16 := dlsym(libGnomeUI2Handle, 'gnome_color_picker_get_i16'); @gnome_color_picker_set_i16 := dlsym(libGnomeUI2Handle, 'gnome_color_picker_set_i16'); @libgnomeui_module_info_get := dlsym(libGnomeUI2Handle, 'libgnomeui_module_info_get'); @gnome_icon_entry_new := dlsym(libGnomeUI2Handle, 'gnome_icon_entry_new'); @gnome_icon_entry_set_pixmap_subdir := dlsym(libGnomeUI2Handle, 'gnome_icon_entry_set_pixmap_subdir'); @gnome_icon_entry_get_filename := dlsym(libGnomeUI2Handle, 'gnome_icon_entry_get_filename'); @gnome_icon_entry_set_filename := dlsym(libGnomeUI2Handle, 'gnome_icon_entry_set_filename'); @gnome_date_edit_new := dlsym(libGnomeUI2Handle, 'gnome_date_edit_new'); @gnome_date_edit_set_time := dlsym(libGnomeUI2Handle, 'gnome_date_edit_set_time'); @gnome_date_edit_get_time := dlsym(libGnomeUI2Handle, 'gnome_date_edit_get_time'); DebugMsg(['libgnomeui-2.so loaded, @gnome_about_new = 0x', IntToHex(QWORD(@gnome_about_new), 8), ', @gnome_color_picker_new = 0x', IntToHex(QWORD(@gnome_color_picker_new), 8), ', @gnome_icon_entry_new = 0x', IntToHex(QWORD(@gnome_icon_entry_new), 8), ', @gnome_date_edit_new = 0x', IntToHex(QWORD(@gnome_date_edit_new), 8), ', @libgnomeui_module_info_get = 0x', IntToHex(QWORD(@libgnomeui_module_info_get), 8)]); end; SetupGnomeLibs; end; initialization finalization // if libGnomeUI2Handle <> 0 then FreeLibrary(libGnomeUI2Handle); // if libGnome2Handle <> 0 then FreeLibrary(libGnome2Handle); end.