summaryrefslogtreecommitdiff
path: root/UGnome.pas
diff options
context:
space:
mode:
authorTomas Bzatek <tbzatek@users.sourceforge.net>2008-06-07 20:34:49 +0200
committerTomas Bzatek <tbzatek@users.sourceforge.net>2008-06-07 20:34:49 +0200
commitecde167da74c86bc047aaf84c5e548cf65a5da98 (patch)
treea015dfda84f28a65811e3aa0d369f8f211ec8c60 /UGnome.pas
downloadtuxcmd-release-0.6.36-dev.tar.xz
Diffstat (limited to 'UGnome.pas')
-rw-r--r--UGnome.pas1274
1 files changed, 1274 insertions, 0 deletions
diff --git a/UGnome.pas b/UGnome.pas
new file mode 100644
index 0000000..f90886b
--- /dev/null
+++ b/UGnome.pas
@@ -0,0 +1,1274 @@
+(*
+ Tux Commander - UGnome - libgnome and libgnomeui related functions and classes
+ Copyright (C) 2008 Tomas Bzatek <tbzatek@users.sourceforge.net>
+ 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, pango, Classes, Libc,
+ GTKForms, GTKControls, GTKStdCtrls, GTKExtCtrls, GTKClasses, GTKDialogs, GTKUtils, GTKConsts;
+
+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: TDateTime;
+ procedure SetTime(Value: TDateTime);
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ published
+ property Time: TDateTime 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(const Text: string; const DontShowAgainText: string; var DontShowAgainChecked: boolean;
+ Buttons: TMessageButtons = [mbOK]; Style: TMessageStyle = mbInfo;
+ Default: TMessageButton = mbNone; Escape: TMessageButton = mbNone): TMessageButton;
+
+
+
+type PGnomeColorPicker = PGtkWidget;
+ PGnomeIconEntry = PGtkWidget;
+ PGnomeDateEdit = PGtkWidget;
+
+const AFTER_ALL_TABS = -1;
+ NOT_IN_APP_WINDOWS = -2;
+
+var libGnomeUI2Handle, libGnome2Handle, libGtk2Handle: 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;
+
+
+
+procedure LoadGnomeLibs;
+
+implementation
+
+uses SysUtils, DateUtils, UConfig, UCoreUtils, ULocale;
+
+
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+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: TDateTime;
+begin
+ Result := UnixToDateTime(gnome_date_edit_get_time(FWidget));
+end;
+
+procedure TGnomeDateEdit.SetTime(Value: TDateTime);
+begin
+ gnome_date_edit_set_time(FWidget, DateTimeToUnix(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;
+
+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 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 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 button_press_cb(notebook: PGtkWidget; event: PGdkEventButton; data: gpointer): gboolean; cdecl;
+var tab_clicked: integer;
+ ntb: TEphyNotebook;
+begin
+ ntb := TEphyNotebook(data);
+ if ntb.FBusy then Exit;
+ Result := False;
+
+ 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) and (event^._type = GDK_BUTTON_PRESS) and (tab_clicked >= 0) 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);
+
+ 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;
+ Result := True;
+ end 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);
+ if Assigned(ntb.FOnTabSwitchedEvent) then ntb.FOnTabSwitchedEvent(ntb, page, False);
+ 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(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;
+ DialogParent: PGtkWindow;
+ CheckBox: PGtkCheckButton;
+ i: integer;
+begin
+ if Application.Terminated then
+ begin
+ Result := Escape;
+ Exit;
+ end;
+ if Screen.FormCount > 0
+ then DialogParent := PGtkWindow(Screen.Forms[0].FWidget)
+ else DialogParent := nil;
+ Dialog := gtk_message_dialog_new(DialogParent, 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 gtk_dialog_add_button(PGtkDialog(Dialog), MessageButtonID[i], i);
+ 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;
+
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+
+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
+ 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;
+ libGtk2Handle := dlopen('libgtk-x11-2.0.so.0', RTLD_LAZY);
+ if libGtk2Handle = nil then libGtk2Handle := dlopen('libgtk-x11-2.0.so.0', 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');
+ 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;
+ SetupGnomeLibs;
+end;
+
+
+initialization
+finalization
+// if libGnomeUI2Handle <> 0 then FreeLibrary(libGnomeUI2Handle);
+// if libGnome2Handle <> 0 then FreeLibrary(libGnome2Handle);
+end.
+