diff options
| author | Tomas Bzatek <tbzatek@users.sourceforge.net> | 2008-06-07 20:34:49 +0200 |
|---|---|---|
| committer | Tomas Bzatek <tbzatek@users.sourceforge.net> | 2008-06-07 20:34:49 +0200 |
| commit | ecde167da74c86bc047aaf84c5e548cf65a5da98 (patch) | |
| tree | a015dfda84f28a65811e3aa0d369f8f211ec8c60 /UGnome.pas | |
| download | tuxcmd-0.6.36.tar.xz | |
Initial commitv0.6.36release-0.6.36-dev
Diffstat (limited to 'UGnome.pas')
| -rw-r--r-- | UGnome.pas | 1274 |
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. + |
