1{ $Id$}
2{
3 *****************************************************************************
4 *                             Gtk2WSDialogs.pp                              *
5 *                             ----------------                              *
6 *                                                                           *
7 *                                                                           *
8 *****************************************************************************
9
10 *****************************************************************************
11  This file is part of the Lazarus Component Library (LCL)
12
13  See the file COPYING.modifiedLGPL.txt, included in this distribution,
14  for details about the license.
15 *****************************************************************************
16}
17unit Gtk2WSDialogs;
18
19{$mode objfpc}{$H+}
20
21interface
22
23uses
24  // RTL
25  Gtk2, Glib2, gdk2, pango,
26  SysUtils, Classes,
27  // LCL
28  Gtk2Extra,
29  Graphics, Controls, Dialogs, ExtDlgs, LCLType,
30  LazFileUtils, LazUTF8, LCLStrConsts, LCLProc, InterfaceBase,
31  // Widgetset
32  Gtk2Int, Gtk2Globals, Gtk2Def, Gtk2Proc,
33  WSDialogs;
34
35type
36  { TGtk2WSCommonDialog }
37
38  TGtk2WSCommonDialog = class(TWSCommonDialog)
39  private
40    class procedure SetColorDialogColor(ColorSelection: PGtkColorSelectionDialog; Color: TColor);
41    class procedure SetColorDialogPalette(ColorSelection: PGtkColorSelectionDialog; Palette: TStrings);
42  protected
43    class procedure SetCallbacks(const AGtkWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo); virtual;
44    class procedure SetSizes(const AGtkWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo); virtual;
45  published
46    class function  CreateHandle(const {%H-}ACommonDialog: TCommonDialog): THandle; override;
47    class procedure ShowModal(const ACommonDialog: TCommonDialog); override;
48    class procedure DestroyHandle(const ACommonDialog: TCommonDialog); override;
49  end;
50
51  { TGtk2WSFileDialog }
52
53  TGtk2WSFileDialog = class(TWSFileDialog)
54  protected
55    class procedure SetCallbacks(const AGtkWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo); virtual;
56  published
57    class function CreateHandle(const ACommonDialog: TCommonDialog): THandle; override;
58  end;
59
60  { TGtk2WSOpenDialog }
61
62  TGtk2WSOpenDialog = class(TWSOpenDialog)
63  protected
64    class function CreateOpenDialogFilter(OpenDialog: TOpenDialog; SelWidget: PGtkWidget): string; virtual;
65    class procedure CreateOpenDialogHistory(OpenDialog: TOpenDialog; SelWidget: PGtkWidget); virtual;
66    class procedure CreatePreviewDialogControl(PreviewDialog: TPreviewFileDialog; SelWidget: PGtkWidget); virtual;
67  published
68    class function CreateHandle(const ACommonDialog: TCommonDialog): THandle; override;
69    class function QueryWSEventCapabilities(const {%H-}ACommonDialog: TCommonDialog): TCDWSEventCapabilities; override;
70  end;
71
72  { TGtk2WSSaveDialog }
73
74  TGtk2WSSaveDialog = class(TWSSaveDialog)
75  published
76    class function QueryWSEventCapabilities(const {%H-}ACommonDialog: TCommonDialog): TCDWSEventCapabilities; override;
77  end;
78
79  { TGtk2WSSelectDirectoryDialog }
80
81  TGtk2WSSelectDirectoryDialog = class(TWSSelectDirectoryDialog)
82  published
83    class function QueryWSEventCapabilities(const {%H-}ACommonDialog: TCommonDialog): TCDWSEventCapabilities; override;
84  end;
85
86  { TGtk2WSColorDialog }
87
88  TGtk2WSColorDialog = class(TWSColorDialog)
89  protected
90    class procedure SetCallbacks(const AGtkWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo); virtual;
91  published
92    class function CreateHandle(const ACommonDialog: TCommonDialog): THandle; override;
93    class function QueryWSEventCapabilities(const {%H-}ACommonDialog: TCommonDialog): TCDWSEventCapabilities; override;
94  end;
95
96  { TGtk2WSColorButton }
97
98  TGtk2WSColorButton = class(TWSColorButton)
99  published
100  end;
101
102  { TGtk2WSFontDialog }
103
104  TGtk2WSFontDialog = class(TWSFontDialog)
105  protected
106    class procedure SetCallbacks(const AGtkWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo); virtual;
107  published
108    class function  CreateHandle(const ACommonDialog: TCommonDialog): THandle; override;
109    class function QueryWSEventCapabilities(const {%H-}ACommonDialog: TCommonDialog): TCDWSEventCapabilities; override;
110  end;
111
112// forward declarations
113
114procedure UpdateDetailView(OpenDialog: TOpenDialog);
115
116implementation
117
118{$I gtk2defines.inc}
119
120{-------------------------------------------------------------------------------
121  procedure UpdateDetailView
122  Params: OpenDialog: TOpenDialog
123  Result: none
124
125  Shows some OS dependent information about the current file
126-------------------------------------------------------------------------------}
127procedure UpdateDetailView(OpenDialog: TOpenDialog);
128var
129  FileDetailLabel: PGtkWidget;
130  Filename, OldFilename, Details: String;
131  Widget: PGtkWidget;
132begin
133  //DebugLn(['UpdateDetailView ']);
134  Widget := {%H-}PGtkWidget(OpenDialog.Handle);
135  FileName := gtk_file_chooser_get_filename(PGtkFileChooser(Widget));
136  Filename:=SysToUTF8(Filename);
137
138  OldFilename := OpenDialog.Filename;
139  if Filename = OldFilename then
140    Exit;
141  OpenDialog.Filename := Filename;
142  // tell application, that selection has changed
143  OpenDialog.DoSelectionChange;
144  if (OpenDialog.OnFolderChange <> nil) and
145     (ExtractFilePath(Filename) <> ExtractFilePath(OldFilename)) then
146    OpenDialog.DoFolderChange;
147  // show some information
148  FileDetailLabel := g_object_get_data({%H-}PGObject(OpenDialog.Handle), 'FileDetailLabel');
149  if FileDetailLabel = nil then
150    Exit;
151  if FileExistsUTF8(Filename) then
152    Details := GetFileDescription(Filename)
153  else
154    Details := Format(rsFileInfoFileNotFound, [Filename]);
155  gtk_label_set_text(PGtkLabel(FileDetailLabel), PChar(Details));
156end;
157
158// ---------------------- signals ----------------------------------------------
159
160procedure gtkFileChooserSelectionChangedCB({%H-}Chooser: PGtkFileChooser;
161  Data: Pointer); cdecl;
162var
163  theDialog: TFileDialog;
164begin
165  theDialog:=TFileDialog(Data);
166  if theDialog is TOpenDialog then
167    UpdateDetailView(TOpenDialog(theDialog));
168end;
169
170procedure Gtk2FileChooserResponseCB(widget: PGtkFileChooser; arg1: gint;
171  data: gpointer); cdecl;
172
173  procedure AddFile(List: TStrings; const NewFile: string);
174  var
175    i: Integer;
176  begin
177    for i := 0 to List.Count-1 do
178      if List[i] = NewFile then
179        Exit;
180    List.Add(NewFile);
181  end;
182
183  function SkipDirectory(const AName: String): Boolean;
184  // gtk2-2.20 have problems.
185  // issue http://bugs.freepascal.org/view.php?id=17278
186  begin
187    Result := False;
188    if (gtk_major_version = 2) and (gtk_minor_version >= 20) and
189      (gtk_file_chooser_get_action(Widget) =  GTK_FILE_CHOOSER_ACTION_OPEN) and
190      DirPathExists(AName) then
191      Result := True;
192  end;
193
194var
195  TheDialog: TFileDialog;
196  cFilename: PChar;
197  cFilenames: PGSList;
198  cFilenames1: PGSList;
199  Files: TStringList;
200  aFilename: String;
201begin
202  //DebugLn(['Gtk2FileChooserResponseCB ']);
203  theDialog := TFileDialog(data);
204
205  if arg1 = GTK_RESPONSE_CANCEL then
206  begin
207    TheDialog.UserChoice := mrCancel;
208    Exit;
209  end;
210
211  if theDialog is TOpenDialog then
212  begin
213    if ofAllowMultiSelect in TOpenDialog(theDialog).Options then
214    begin
215      TheDialog.FileName := '';
216      Files := TStringList(TheDialog.Files);
217      Files.Clear;
218      cFilenames := gtk_file_chooser_get_filenames(widget);
219      if Assigned(cFilenames) then
220      begin
221        cFilenames1 := cFilenames;
222        while Assigned(cFilenames1) do
223        begin
224          cFilename := PChar(cFilenames1^.data);
225          if Assigned(cFilename) then
226          begin
227            aFilename:=SysToUTF8(cFilename);
228            if not SkipDirectory(aFileName) then
229              AddFile(Files, aFilename);
230            g_free(cFilename);
231          end;
232          cFilenames1 := cFilenames1^.next;
233        end;
234        g_slist_free(cFilenames);
235      end;
236    end
237    else
238      TheDialog.Files.Clear;
239  end;
240
241  cFilename := gtk_file_chooser_get_filename(widget);
242
243  if Assigned(cFilename) then
244  begin
245    aFilename:=SysToUTF8(cFilename);
246    if SkipDirectory(aFileName) then
247      TheDialog.FileName := ''
248    else
249      TheDialog.FileName := cFilename;
250    g_free(cFilename);
251    if (TheDialog is TOpenDialog) and (not (ofAllowMultiSelect in TOpenDialog(theDialog).Options)) then
252      TheDialog.Files.Add(TheDialog.FileName);
253  end;
254
255  //?? StoreCommonDialogSetup(theDialog);
256  theDialog.UserChoice := mrOK;
257end;
258
259procedure Gtk2FileChooserNotifyCB(dialog: PGObject; pspec: PGParamSpec;
260  user_data: gpointer); cdecl;
261var
262  TheDialog: TFileDialog;
263  GtkFilter: PGtkFileFilter;
264  GtkFilterList: PGSList;
265  NewFilterIndex: Integer;
266begin
267  //DebugLn(['Gtk2FileChooserNotifyCB ']);
268  if pspec^.name = 'filter' then
269  begin // filter changed
270    theDialog := TFileDialog(user_data);
271    GtkFilter := gtk_file_chooser_get_filter(dialog);
272    GtkFilterList := gtk_file_chooser_list_filters(dialog);
273    if (GtkFilter = nil) and (theDialog.Filter <> '') then
274    begin
275      // Either we don't have filter or gtk reset it.
276      // Gtk resets filter if we set both filename and filter but filename
277      // does not fit into filter. Gtk comparision has bug - it compares only by
278      // mime-type, not by pattern. LCL set all filters by pattern.
279      GtkFilter := g_slist_nth_data(GtkFilterList, theDialog.FilterIndex - 1);
280      gtk_file_chooser_set_filter(dialog, GtkFilter);
281    end
282    else
283    begin
284      NewFilterIndex := g_slist_index(GtkFilterList, GtkFilter);
285      theDialog.IntfFileTypeChanged(NewFilterIndex + 1);
286    end;
287    g_slist_free(GtkFilterList);
288  end;
289end;
290
291// ------------------------ Signals --------------------------------------------
292
293{-------------------------------------------------------------------------------
294  function GTKDialogSelectRowCB
295  Params: widget: PGtkWidget; data: gPointer
296  Result: GBoolean
297
298  This function is called, whenever a row is selected in a commondialog
299-------------------------------------------------------------------------------}
300function gtkDialogSelectRowCB(widget: PGtkWidget; Row, Column: gInt;
301  bevent: pgdkEventButton; data: gPointer): GBoolean; cdecl;
302var
303  theDialog: TCommonDialog;
304  MenuWidget: PGtkWidget;
305  AFilterEntry: TFileSelFilterEntry;
306  FileSelWidget: PGtkFileSelection;
307  ShiftState: TShiftState;
308  loop : gint;
309  startRow : gint;
310  endRow : gint;
311begin
312  //debugln('GTKDialogSelectRowCB A ');
313  Result:=CallBackDefaultReturn;
314  if (Data=nil) or (BEvent=nil) or (Column=0) or (Row=0) then ;
315  theDialog:=TCommonDialog(GetLCLObject(Widget));
316  if (theDialog is TOpenDialog) then begin
317    // only process the callback if there is event data. If there isn't any
318    // event data that means it was called due to a direct function call of the
319    // widget and not an actual mouse click on the widget.
320    FileSelWidget:={%H-}PGtkFileSelection(theDialog.Handle);
321    if (bevent <> nil) and (gdk_event_get_type(bevent) = GDK_2BUTTON_PRESS)
322    and (FileSelWidget^.dir_list = widget) then begin
323      MenuWidget := g_object_get_data(PGObject(FileSelWidget),
324                                        'LCLFilterMenu');
325      if MenuWidget <> nil then begin
326        AFilterEntry := TFileSelFilterEntry(g_object_get_data(PGObject(
327            gtk_menu_get_active(PGtkMenu(MenuWidget))), 'LCLIsFilterMenuItem'));
328        if (AFilterEntry<>nil) and (AFilterEntry.Mask<>nil) then
329          PopulateFileAndDirectoryLists(FileSelWidget,AFilterEntry.Mask);
330      end;
331    end
332    else if (bevent <> nil)
333    and (ofAllowMultiSelect in TOpenDialog(theDialog).Options)
334    and (FileSelWidget^.file_list=widget) then begin
335      // multi selection
336      ShiftState := GTKEventStateToShiftState(BEvent^.State);
337      if ssShift in ShiftState then begin
338        if LastFileSelectRow <> -1 then begin
339          startRow := LastFileSelectRow;
340          endRow := row;
341          if LastFileSelectRow > row then begin
342            startRow := row;
343            endRow := LastFileSelectRow;
344          end;
345          for loop := startRow to endRow do begin
346            gtk_clist_select_row(PGtkCList(widget), loop, column);
347          end;
348        end;
349      end
350      else if not (ssCtrl in ShiftState) then begin
351        gtk_clist_unselect_all(PGtkCList(widget));
352        gtk_clist_select_row(PGtkCList(widget), row, column);
353      end;
354      LastFileSelectRow := row;
355    end;
356    UpdateDetailView(TOpenDialog(theDialog));
357  end;
358end;
359
360{-------------------------------------------------------------------------------
361  function gtkDialogHelpclickedCB
362  Params: widget: PGtkWidget; data: gPointer
363  Result: GBoolean
364
365  This function is called, whenever the user clicks the help button in a
366  commondialog
367-------------------------------------------------------------------------------}
368function gtkDialogHelpclickedCB(widget: PGtkWidget; data: gPointer): GBoolean; cdecl;
369var
370  theDialog : TCommonDialog;
371begin
372  Result := CallBackDefaultReturn;
373  if (Widget=nil) then ;
374  theDialog := TCommonDialog(data);
375  if theDialog is TOpenDialog then begin
376    if TOpenDialog(theDialog).OnHelpClicked<>nil then
377      TOpenDialog(theDialog).OnHelpClicked(theDialog);
378  end;
379end;
380
381{-------------------------------------------------------------------------------
382  function gtkDialogApplyclickedCB
383  Params: widget: PGtkWidget; data: gPointer
384  Result: GBoolean
385
386  This function is called, whenever the user clicks the Apply button in a
387  commondialog
388-------------------------------------------------------------------------------}
389function gtkDialogApplyclickedCB(widget: PGtkWidget; data: gPointer): GBoolean;
390  cdecl;
391var
392  theDialog : TCommonDialog;
393  FontName: string;
394  ALogFont: TLogFont;
395
396  FontDesc: PPangoFontDescription;
397begin
398  Result := CallBackDefaultReturn;
399  if (Widget=nil) then ;
400  theDialog := TCommonDialog(data);
401  if (theDialog is TFontDialog)
402  and (fdApplyButton in TFontDialog(theDialog).Options)
403  and (Assigned(TFontDialog(theDialog).OnApplyClicked)) then begin
404    FontName := gtk_font_selection_dialog_get_font_name(
405                                    {%H-}pgtkfontselectiondialog(theDialog.Handle));
406    if IsFontNameXLogicalFontDesc(FontName) then begin
407      // extract basic font attributes from the font name in XLFD format
408      ALogFont:=XLFDNameToLogFont(FontName);
409      TFontDialog(theDialog).Font.Assign(ALogFont);
410      // set the font name in XLFD format
411      // a font name in XLFD format overrides in the gtk interface all other font
412      // settings.
413      TFontDialog(theDialog).Font.Name := FontName;
414    end else begin
415      FontDesc := pango_font_description_from_string(PChar(FontName));
416      with TFontDialog(theDialog).Font do
417      begin
418        BeginUpdate;
419        Size := pango_font_description_get_size(FontDesc) div PANGO_SCALE;
420        if pango_font_description_get_weight(FontDesc) >= PANGO_WEIGHT_BOLD then
421          Style := Style + [fsBold]
422        else
423          Style := Style - [fsBold];
424        if pango_font_description_get_style(FontDesc) > PANGO_STYLE_NORMAL then
425          Style := Style + [fsItalic]
426        else
427          Style := Style - [fsItalic];
428        Name := pango_font_description_get_family(FontDesc);
429        EndUpdate;
430      end;
431      pango_font_description_free(FontDesc);
432    end;
433    TFontDialog(theDialog).OnApplyClicked(theDialog);
434  end;
435end;
436
437function gtkDialogOKclickedCB( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl;
438var
439  theDialog : TCommonDialog;
440  Fpointer : Pointer;
441  // colordialog
442  colorsel : PGtkColorSelection;
443  newColor : TGdkColor;
444  // fontdialog
445  FontName : String;
446  ALogFont  : TLogFont;
447  // filedialog
448  rowNum   : gint;
449  fileInfo : PGChar;
450
451  fileList : PPgchar;
452  FontDesc: PPangoFontDescription;
453
454  DirName  : string;
455  FileName : string;
456  Files: TStringList;
457  CurFilename: string;
458  //SelectedFont: PGdkFont;
459
460  function CheckOpenedFilename(var AFilename: string): boolean;
461  begin
462    Result:=true;
463
464    // maybe file already exists
465    if (ofOverwritePrompt in TOpenDialog(theDialog).Options)
466    and FileExistsUTF8(AFilename) then
467    begin
468      Result := MessageDlg(rsfdOverwriteFile,
469                         Format(rsfdFileAlreadyExists,[AFileName]),
470                         mtConfirmation,[mbOk,mbCancel],0)=mrOk;
471      if not Result then exit;
472    end;
473  end;
474
475  procedure AddFile(List: TStrings; const NewFile: string);
476  var
477    i: Integer;
478  begin
479    for i:=0 to List.Count-1 do
480      if List[i]=NewFile then exit;
481    List.Add(NewFile);
482  end;
483
484begin
485  Result := True;
486  if (Widget=nil) then ;
487  theDialog := TCommonDialog(data);
488  FPointer := {%H-}Pointer(theDialog.Handle);
489
490  if theDialog is TFileDialog then
491  begin
492    FileName:=gtk_file_chooser_get_filename(PGtkFileChooser(FPointer));
493    FileName:=SysToUTF8(Filename);
494
495    if theDialog is TOpenDialog then
496    begin
497      // check extra options
498      if ofAllowMultiSelect in TOpenDialog(theDialog).Options then
499      begin
500        DirName:=ExtractFilePath(FileName);
501        TFileDialog(data).FileName := '';
502        Files:=TStringList(TFileDialog(theDialog).Files);
503        Files.Clear;
504        if (Filename<>'') then begin
505          Result:=CheckOpenedFilename(Filename);
506          if not Result then exit;
507          AddFile(Files,FileName);
508        end;
509
510        fileList := gtk_file_selection_get_selections(PGtkFileSelection(FPointer));
511        rowNum := 0;
512        While FileList^ <> nil do
513        begin
514          fileInfo := FileList^;
515          CurFilename:=fileInfo; // convert PChar to AnsiString (not typecast)
516          CurFilename:=SysToUTF8(CurFilename);
517          if (CurFilename<>'') and (Files.IndexOf(CurFilename)<0) then begin
518            CurFilename:=DirName+fileInfo;
519            Result:=CheckOpenedFilename(CurFilename);
520            if not Result then exit;
521            Files.Add(CurFilename);
522          end;
523          inc(FileList);
524          inc(rowNum);
525        end;
526        Dec(FileList, rowNum);
527        g_strfreev(fileList);
528      end
529      else
530      begin
531        Result:=CheckOpenedFilename(Filename);
532        if not Result then exit;
533        TFileDialog(data).FileName := Filename;
534      end;
535    end
536    else
537    begin
538      TFileDialog(data).FileName := Filename;
539    end;
540  end
541  else if theDialog is TColorDialog then
542  begin
543    colorSel := PGtkColorSelection(PGtkColorSelectionDialog(FPointer)^.colorsel);
544    gtk_color_selection_get_current_color(colorsel, @newColor);
545    TColorDialog(theDialog).Color := TGDKColorToTColor(newcolor);
546    {$IFDEF VerboseColorDialog}
547    DebugLn('gtkDialogOKclickedCB ',DbgS(TColorDialog(theDialog).Color));
548    {$ENDIF}
549  end
550  else if theDialog is TFontDialog then
551  begin
552    //DebugLn('Trace:Pressed OK in FontDialog');
553    FontName := gtk_font_selection_dialog_get_font_name(
554                                             pgtkfontselectiondialog(FPointer));
555    //debugln('gtkDialogOKclickedCB FontName=',FontName);
556    //SelectedFont:=gdk_font_load(PChar(FontName));
557    //debugln('gtkDialogOKclickedCB ',dbgs(SelectedFont));
558
559    if IsFontNameXLogicalFontDesc(FontName) then
560    begin
561      // extract basic font attributes from the font name in XLFD format
562      ALogFont:=XLFDNameToLogFont(FontName);
563      TFontDialog(theDialog).Font.Assign(ALogFont);
564      // set the font name in XLFD format
565      // a font name in XLFD format overrides in the gtk interface all other font
566      // settings.
567      TFontDialog(theDialog).Font.Name := FontName;
568    end
569    else
570    begin
571      FontDesc := pango_font_description_from_string(PChar(FontName));
572      with TFontDialog(theDialog).Font do
573      begin
574        BeginUpdate;
575        Size := pango_font_description_get_size(FontDesc) div PANGO_SCALE;
576        if pango_font_description_get_weight(FontDesc) >= PANGO_WEIGHT_BOLD then
577          Style := Style + [fsBold]
578        else
579          Style := Style - [fsBold];
580        if pango_font_description_get_style(FontDesc) > PANGO_STYLE_NORMAL then
581          Style := Style + [fsItalic]
582        else
583          Style := Style - [fsItalic];
584        Name := pango_font_description_get_family(FontDesc);
585        EndUpdate;
586      end;
587      pango_font_description_free(FontDesc);
588    end;
589
590    //DebugLn('Trace:-----'+TFontDialog(theDialog).Font.Name+'----');
591  end;
592
593  StoreCommonDialogSetup(theDialog);
594  theDialog.UserChoice := mrOK;
595end;
596
597{-------------------------------------------------------------------------------
598  function gtkDialogCancelclickedCB
599  Params: widget: PGtkWidget; data: gPointer
600  Result: GBoolean
601
602  This function is called, whenever the user clicks the cancel button in a
603  commondialog
604-------------------------------------------------------------------------------}
605function gtkDialogCancelclickedCB(widget: PGtkWidget; data: gPointer): GBoolean;
606  cdecl;
607var
608  theDialog : TCommonDialog;
609begin
610  Result := CallBackDefaultReturn;
611  if (Widget=nil) then ;
612  theDialog := TCommonDialog(data);
613  if theDialog is TFileDialog then
614  begin
615    TFileDialog(data).FileName := '';
616  end;
617  StoreCommonDialogSetup(theDialog);
618  theDialog.UserChoice := mrCancel;
619end;
620
621{-------------------------------------------------------------------------------
622  function GTKDialogRealizeCB
623  Params: Widget: PGtkWidget; Data: Pointer
624  Result: GBoolean
625
626  This function is called, whenever a commondialog window is realized
627-------------------------------------------------------------------------------}
628function GTKDialogRealizeCB(Widget: PGtkWidget; Data: Pointer): GBoolean; cdecl;
629var
630  LCLComponent: TObject;
631begin
632  if (Data=nil) then ;
633  gdk_window_set_events(GetControlWindow(Widget),
634    gdk_window_get_events(GetControlWindow(Widget))
635      or GDK_KEY_RELEASE_MASK or GDK_KEY_PRESS_MASK);
636  LCLComponent:=GetLCLObject(Widget);
637  if LCLComponent is TCommonDialog then
638  begin
639    {$ifdef DebugCommonDialogEvents}
640    debugln(['GTKDialogRealizeCB calling DoShow']);
641    {$endif}
642    TCommonDialog(LCLComponent).DoShow;
643  end;
644  Result:=true;
645end;
646
647{-------------------------------------------------------------------------------
648  function gtkDialogCloseQueryCB
649  Params: widget: PGtkWidget; data: gPointer
650  Result: GBoolean
651
652  This function is called, before a commondialog is destroyed
653  (Only when the user aborts the dialog, not if the dialog closes as the result
654   of a click on one of itś buttons)
655-------------------------------------------------------------------------------}
656function gtkDialogCloseQueryCB(widget: PGtkWidget; data: gPointer): GBoolean;
657  cdecl;
658var
659  theDialog : TCommonDialog;
660  CanClose: boolean;
661begin
662  {$ifdef DebugCommonDialogEvents}
663  debugln(['>>>>gtkDialogCloseQueryCB A']);
664  {$endif}
665  Result := False; // true = do nothing, false = destroy or hide window
666  if (Data=nil) then ;
667  // data is not the commondialog. Get it manually.
668  theDialog := TCommonDialog(GetLCLObject(Widget));
669  if theDialog=nil then exit;
670  if theDialog.OnCanClose<>nil then begin
671    theDialog.UserChoice := mrCancel;
672    CanClose:=True;
673    {$ifdef DebugCommonDialogEvents}
674    debugln(['gtkDialogCloseQueryCB calling DoCanClose']);
675    {$endif}
676    theDialog.DoCanClose(CanClose);
677    Result:=not CanClose;
678  end;
679  if not Result then begin
680    StoreCommonDialogSetup(theDialog);
681    DestroyCommonDialogAddOns(theDialog);
682  end;
683  {$ifdef DebugCommonDialogEvents}
684  debugln(['gtkDialogCloseQueryCB End']);
685  {$endif}
686end;
687
688{-------------------------------------------------------------------------------
689  function gtkDialogDestroyCB
690  Params: widget: PGtkWidget; data: gPointer
691  Result: GBoolean
692
693  This function is called, when a commondialog is destroyed
694-------------------------------------------------------------------------------}
695function gtkDialogDestroyCB(widget: PGtkWidget; data: gPointer): GBoolean; cdecl;
696begin
697  {$ifdef DebugCommonDialogEvents}
698  debugln(['gtkDialogDestroyCB A']);
699  {$endif}
700  Result := True;
701  if (Widget=nil) then ;
702  TCommonDialog(data).UserChoice := mrCancel;
703  TCommonDialog(data).Close;
704  {$ifdef DebugCommonDialogEvents}
705  debugln(['gtkDialogDestroyCB End']);
706  {$endif}
707end;
708
709{-------------------------------------------------------------------------------
710  function GTKDialogKeyUpDownCB
711  Params: Widget: PGtkWidget; Event : pgdkeventkey; Data: gPointer
712  Result: GBoolean
713
714  This function is called, whenever a key is pressed or released in a common
715  dialog window
716-------------------------------------------------------------------------------}
717function GTKDialogKeyUpDownCB(Widget: PGtkWidget; Event : pgdkeventkey;
718  Data: gPointer) : GBoolean; cdecl;
719begin
720  Result:=CallBackDefaultReturn;
721
722  if (Widget=nil) then ;
723  case gdk_event_get_type(Event) of
724
725  GDK_KEY_RELEASE, GDK_KEY_PRESS:
726    begin
727      if Event^.KeyVal = GDK_KEY_Escape
728      then begin
729        StoreCommonDialogSetup(TCommonDialog(data));
730        TCommonDialog(data).UserChoice:=mrCancel;
731      end;
732      if (TCommonDialog(data) is TOpenDialog) then begin
733        UpdateDetailView(TOpenDialog(data));
734      end;
735    end;
736
737  end;
738end;
739
740{-------------------------------------------------------------------------------
741  function GTKDialogFocusInCB
742  Params: widget: PGtkWidget; data: gPointer
743  Result: GBoolean
744
745  This function is called, when a widget of a commondialog gets focus
746-------------------------------------------------------------------------------}
747function GTKDialogFocusInCB(widget: PGtkWidget; data: gPointer): GBoolean;
748  cdecl;
749var
750  theDialog: TCommonDialog;
751begin
752  //debugln('GTKDialogFocusInCB A ');
753  Result:=CallBackDefaultReturn;
754  if (Data=nil) then ;
755  theDialog:=TCommonDialog(GetLCLObject(Widget));
756  if (theDialog is TOpenDialog) then begin
757    UpdateDetailView(TOpenDialog(theDialog));
758  end;
759end;
760
761{-------------------------------------------------------------------------------
762  function GTKDialogMenuActivateCB
763  Params: widget: PGtkWidget; data: gPointer
764  Result: GBoolean
765
766  This function is called, whenever a menu of a commondialog is activated
767-------------------------------------------------------------------------------}
768function GTKDialogMenuActivateCB(widget: PGtkWidget; data: gPointer): GBoolean;
769  cdecl;
770var
771  theDialog: TCommonDialog;
772
773  procedure CheckFilterActivated(FilterWidget: PGtkWidget);
774  var
775    AFilterEntry: TFileSelFilterEntry;
776  begin
777    if FilterWidget=nil then exit;
778    AFilterEntry:=TFileSelFilterEntry(g_object_get_data(PGObject(FilterWidget),
779                                      'LCLIsFilterMenuItem'));
780    if (AFilterEntry<>nil) and (AFilterEntry.Mask<>nil) then
781    begin
782      PopulateFileAndDirectoryLists({%H-}PGtkFileSelection(theDialog.Handle),
783                                    AFilterEntry.Mask);
784      TFileDialog(TheDialog).IntfFileTypeChanged(AFilterEntry.FilterIndex + 1);
785      UpdateDetailView(TOpenDialog(theDialog));
786    end;
787  end;
788
789var
790  AHistoryEntry: PFileSelHistoryEntry;
791  aSysFilename: String;
792begin
793  Result:=false;
794  if (Data=nil) then ;
795  theDialog:=TCommonDialog(GetNearestLCLObject(Widget));
796  if (theDialog is TOpenDialog) then begin
797    // check if history activated
798    AHistoryEntry:=g_object_get_data(PGObject(Widget),
799                                       'LCLIsHistoryMenuItem');
800    if (AHistoryEntry<>nil) and (AHistoryEntry^.Filename<>nil) then begin
801      // user has choosen a history file
802      // -> select it in the filedialog
803      aSysFilename:=UTF8ToSys(AHistoryEntry^.Filename);
804      gtk_file_chooser_set_current_folder({%H-}PGtkFileChooser(theDialog.Handle),
805        Pgchar(aSysFilename));
806
807      UpdateDetailView(TOpenDialog(theDialog));
808    end;
809  end;
810end;
811
812{ TGtk2WSSelectDirectoryDialog }
813
814class function TGtk2WSSelectDirectoryDialog.QueryWSEventCapabilities(
815  const ACommonDialog: TCommonDialog): TCDWSEventCapabilities;
816begin
817  Result := [cdecWSPerformsDoShow];
818end;
819
820{ TGtk2WSSaveDialog }
821
822class function TGtk2WSSaveDialog.QueryWSEventCapabilities(
823  const ACommonDialog: TCommonDialog): TCDWSEventCapabilities;
824begin
825  Result := [cdecWSPerformsDoShow];
826end;
827
828// ---------------------- END OF signals ---------------------------------------
829
830{ TGtk2WSOpenDialog }
831
832class function TGtk2WSOpenDialog.CreateOpenDialogFilter(
833  OpenDialog: TOpenDialog; SelWidget: PGtkWidget): string;
834var
835  ListOfFileSelFilterEntry: TFPList;
836  i, j, k: integer;
837  GtkFilter, GtkSelFilter: PGtkFileFilter;
838  MaskList: TStringList;
839  FilterEntry: TFileSelFilterEntry;
840  FilterIndex: Integer;
841  aMask: String;
842begin
843  FilterIndex := OpenDialog.FilterIndex;
844  ExtractFilterList(OpenDialog.Filter, ListOfFileSelFilterEntry, false);
845  GtkSelFilter := nil;
846  if ListOfFileSelFilterEntry.Count > 0 then
847  begin
848    j := 1;
849    MaskList := TStringList.Create;
850    MaskList.Delimiter := ';';
851    for i := 0 to ListOfFileSelFilterEntry.Count-1 do
852    begin
853      GtkFilter := gtk_file_filter_new();
854
855      FilterEntry := TFileSelFilterEntry(ListOfFileSelFilterEntry[i]);
856      MaskList.DelimitedText := FilterEntry.Mask;
857
858      for k := 0 to MaskList.Count - 1 do begin
859        aMask:=UTF8ToSys(MaskList.Strings[k]);
860        gtk_file_filter_add_pattern(GtkFilter, PChar(aMask));
861      end;
862
863      gtk_file_filter_set_name(GtkFilter, FilterEntry.Description);
864
865      gtk_file_chooser_add_filter(SelWidget, GtkFilter);
866
867      if j = FilterIndex then
868        GtkSelFilter := GtkFilter;
869
870      Inc(j);
871      GtkFilter := nil;
872    end;
873    MaskList.Free;
874  end;
875
876  FreeListOfFileSelFilterEntry(ListOfFileSelFilterEntry);
877  //g_object_set_data(PGObject(SelWidget), 'LCLFilterList', ListOfFileSelFilterEntry);
878
879  if GtkSelFilter <> nil then
880    gtk_file_chooser_set_filter(SelWidget, GtkSelFilter);
881
882  Result := 'hm'; { Don't use '' as null return as this is used for *.* }
883end;
884
885class procedure TGtk2WSOpenDialog.CreateOpenDialogHistory(
886  OpenDialog: TOpenDialog; SelWidget: PGtkWidget);
887var
888  HistoryList: TFPList; // list of THistoryListEntry
889  AHistoryEntry: PFileSelHistoryEntry;
890  i: integer;
891  s: string;
892  HBox, LabelWidget, HistoryPullDownWidget,
893  MenuWidget, MenuItemWidget: PGtkWidget;
894begin
895  if OpenDialog.HistoryList.Count>0 then begin
896
897    // create the HistoryList where the current state of the history is stored
898    HistoryList:=TFPList.Create;
899    for i:=0 to OpenDialog.HistoryList.Count-1 do begin
900      s:=OpenDialog.HistoryList[i];
901      if s<>'' then begin
902        New(AHistoryEntry);
903        HistoryList.Add(AHistoryEntry);
904        AHistoryEntry^.Filename := StrAlloc(length(s)+1);
905        StrPCopy(AHistoryEntry^.Filename, s);
906        AHistoryEntry^.MenuItem:=nil;
907      end;
908    end;
909
910    // create a HBox so that the history is left justified
911    HBox:=gtk_hbox_new(false,0);
912    g_object_set_data(PGObject(SelWidget), 'LCLHistoryHBox', HBox);
913    gtk_file_chooser_set_extra_widget(PGtkDialog(SelWidget),HBox);
914
915    // create the label 'History:'
916    s:=rsgtkHistory;
917    LabelWidget:=gtk_label_new(PChar(s));
918    gtk_box_pack_start(GTK_BOX(HBox),LabelWidget,false,false,5);
919    gtk_widget_show(LabelWidget);
920
921    // create the pull down
922    HistoryPullDownWidget:=gtk_option_menu_new;
923    g_object_set_data(PGObject(SelWidget), 'LCLHistoryPullDown',
924      HistoryPullDownWidget);
925    gtk_box_pack_start(GTK_BOX(HBox),HistoryPullDownWidget,false,false,5);
926    gtk_widget_show(HistoryPullDownWidget);
927    gtk_widget_show_all(HBox);
928
929    // create the menu (the content of the pull down)
930    MenuWidget:=gtk_menu_new;
931    SetLCLObject(MenuWidget,OpenDialog);
932    for i:=0 to HistoryList.Count-1 do begin
933      // create the menu items in the history menu
934      MenuItemWidget:=gtk_menu_item_new_with_label(
935                                PFileSelHistoryEntry(HistoryList[i])^.Filename);
936      // connect the new MenuItem to the HistoryList entry
937      g_object_set_data(PGObject(MenuItemWidget), 'LCLIsHistoryMenuItem',
938        HistoryList[i]);
939      // add activation signal and add to menu
940      g_signal_connect(GTK_OBJECT(MenuItemWidget), 'activate',
941                         gtk_signal_func(@GTKDialogMenuActivateCB),
942                         OpenDialog);
943      gtk_menu_append(MenuWidget, MenuItemWidget);
944      gtk_widget_show(MenuItemWidget);
945    end;
946    gtk_widget_show(MenuWidget);
947    gtk_option_menu_set_menu(GTK_OPTION_MENU(HistoryPullDownWidget),
948                             MenuWidget);
949  end else begin
950    MenuWidget:=nil;
951    HistoryList:=nil
952  end;
953  g_object_set_data(PGObject(SelWidget), 'LCLHistoryMenu', MenuWidget);
954  g_object_set_data(PGObject(SelWidget), 'LCLHistoryList', HistoryList);
955end;
956
957class procedure TGtk2WSOpenDialog.CreatePreviewDialogControl(
958  PreviewDialog: TPreviewFileDialog; SelWidget: PGtkWidget);
959var
960  PreviewWidget: PGtkWidget;
961  AControl: TPreviewFileControl;
962  Win, SubWin: TWinControl;
963  FileChooser: PGtkFileChooser;
964begin
965  AControl := PreviewDialog.PreviewFileControl;
966  if AControl = nil then Exit;
967
968  FileChooser := PGtkFileChooser(SelWidget);
969
970  PreviewWidget := {%H-}PGtkWidget(AControl.Handle);
971
972  g_object_set_data(PGObject(PreviewWidget),'LCLPreviewFixed',
973                      PreviewWidget);
974  gtk_widget_set_size_request(PreviewWidget,AControl.Width,AControl.Height);
975
976  // manually resize the preview objects, it seems, automatic resize is not
977  // working when parent of LCL control is not a LCL control.
978  if (AControl.ControlCount>0) and (AControl.Controls[0] is TWinControl) then begin
979    Win := TWinControl(AControl.Controls[0]);  // groupbox
980    SubWin := TWinControl(Win.Controls[0]);    // image
981    gtk_widget_set_size_request({%H-}PGtkWidget(Win.Handle), AControl.Width, AControl.Height);
982    SubWin.width := AControl.Width-4;          // skip borders
983    SubWin.height := AControl.Height-15;       //
984  end;
985
986  gtk_file_chooser_set_preview_widget(FileChooser, PreviewWidget);
987end;
988
989{
990  Adds some functionality to a gtk file selection dialog.
991  - multiselection
992  - range selection
993  - close on escape
994  - file information
995  - history pulldown
996  - filter pulldown
997  - preview control
998
999  requires: gtk+ 2.6
1000}
1001class function TGtk2WSOpenDialog.CreateHandle(const ACommonDialog: TCommonDialog): THandle;
1002var
1003  FileSelWidget: PGtkFileChooser;
1004  OpenDialog: TOpenDialog absolute ACommonDialog;
1005  HelpButton: PGtkWidget;
1006  InitialFilename: String;
1007  aSysFilename: String;
1008  //FrameWidget: PGtkWidget;
1009  //HBox: PGtkWidget;
1010  //FileDetailLabel: PGtkWidget;
1011begin
1012  Result := TGtk2WSFileDialog.CreateHandle(ACommonDialog);
1013  FileSelWidget := {%H-}PGtkFileChooser(Result);
1014
1015  if OpenDialog.InheritsFrom(TSaveDialog) then
1016  begin
1017    if OpenDialog.InitialDir <> '' then begin
1018      aSysFilename:=UTF8ToSys(OpenDialog.InitialDir);
1019      gtk_file_chooser_set_current_folder(FileSelWidget, Pgchar(aSysFilename));
1020    end;
1021  end;
1022
1023  // Help button
1024  if (ofShowHelp in OpenDialog.Options) then
1025  begin
1026    HelpButton := gtk_dialog_add_button(FileSelWidget, GTK_STOCK_HELP, GTK_RESPONSE_NONE);
1027
1028    g_signal_connect(PGtkObject(HelpButton),
1029      'clicked', gtk_signal_func(@gtkDialogHelpclickedCB), OpenDialog);
1030  end;
1031
1032  if ofAllowMultiSelect in OpenDialog.Options then
1033    gtk_file_chooser_set_select_multiple(FileSelWidget, gboolean(gtrue));
1034
1035  // History List - a frame with an option menu
1036  CreateOpenDialogHistory(OpenDialog, FileSelWidget);
1037
1038  // Filter
1039  CreateOpenDialogFilter(OpenDialog, FileSelWidget);
1040
1041  // connect change event
1042  g_signal_connect(PGtkObject(FileSelWidget),
1043    'selection-changed', gtk_signal_func(@gtkFileChooserSelectionChangedCB),
1044    OpenDialog);
1045
1046  // Sets the dialog options
1047
1048  // ofForceShowHidden
1049  if (ofForceShowHidden in OpenDialog.Options) then
1050    gtk_file_chooser_set_show_hidden(FileSelWidget, True);
1051
1052  (*  TODO
1053  // Details - a frame with a label
1054  if (ofViewDetail in OpenDialog.Options) then begin
1055
1056    // create the frame around the information
1057    FrameWidget:=gtk_frame_new(PChar(rsFileInformation));
1058    //gtk_box_pack_start(GTK_BOX(FileSelWidget^.main_vbox),
1059    //                   FrameWidget,false,false,0);
1060    gtk_box_pack_start(GTK_BOX(gtk_file_chooser_get_extra_widget(
1061             PGtkFileChooser(SelWidget))), FrameWidget,false,false,0);
1062    gtk_widget_show(FrameWidget);
1063    // create a HBox, so that the information is left justified
1064    HBox:=gtk_hbox_new(false,0);
1065    gtk_container_add(GTK_CONTAINER(FrameWidget), HBox);
1066    // create the label for the file information
1067    FileDetailLabel:=gtk_label_new(PChar(rsDefaultFileInfoValue));
1068    gtk_box_pack_start(GTK_BOX(HBox),FileDetailLabel,false,false,5);
1069    gtk_widget_show_all(HBox);
1070  end else
1071    FileDetailLabel:=nil;
1072  g_object_set_data(PGObject(SelWidget), 'FileDetailLabel',
1073                      FileDetailLabel);
1074  *)
1075  // preview
1076  if (OpenDialog is TPreviewFileDialog) then
1077    CreatePreviewDialogControl(TPreviewFileDialog(OpenDialog), PGtkWidget(FileSelWidget));
1078
1079  // set initial filename (gtk expects an absolute filename)
1080  InitialFilename := TrimFilename(OpenDialog.FileName);
1081  if InitialFilename <> '' then
1082  begin
1083    if not FilenameIsAbsolute(InitialFilename) and (OpenDialog.InitialDir <> '') then
1084      InitialFilename := TrimFilename(OpenDialog.InitialDir + PathDelim + InitialFilename);
1085    if not FilenameIsAbsolute(InitialFilename) then
1086      InitialFilename := CleanAndExpandFilename(InitialFilename);
1087    aSysFilename:=UTF8ToSys(InitialFilename);
1088    gtk_file_chooser_set_filename(FileSelWidget, PChar(aSysFilename));
1089  end;
1090
1091  //if InitialFilter <> 'none' then
1092  //  PopulateFileAndDirectoryLists(FileSelWidget, InitialFilter);
1093end;
1094
1095class function TGtk2WSOpenDialog.QueryWSEventCapabilities(
1096  const ACommonDialog: TCommonDialog): TCDWSEventCapabilities;
1097begin
1098  Result := [cdecWSPerformsDoShow];
1099end;
1100
1101{ TGtk2WSFileDialog }
1102
1103class procedure TGtk2WSFileDialog.SetCallbacks(const AGtkWidget: PGtkWidget;
1104  const AWidgetInfo: PWidgetInfo);
1105begin
1106  TGtk2WSCommonDialog.SetCallbacks(AGtkWidget, AWidgetInfo);
1107  g_signal_connect(AGtkWidget, 'response', gtk_signal_func(@Gtk2FileChooserResponseCB), AWidgetInfo^.LCLObject);
1108  g_signal_connect(AGtkWidget, 'notify', gtk_signal_func(@Gtk2FileChooserNotifyCB), AWidgetInfo^.LCLObject);
1109end;
1110
1111{
1112  Creates a new TFile/Open/SaveDialog
1113  requires: gtk+ 2.6
1114}
1115class function TGtk2WSFileDialog.CreateHandle(const ACommonDialog: TCommonDialog
1116  ): THandle;
1117var
1118  FileDialog: TFileDialog absolute ACommonDialog;
1119  Action: TGtkFileChooserAction;
1120  Button1: String;
1121  Widget: PGtkWidget;
1122  WidgetInfo: PWidgetInfo;
1123  aSysFilename: String;
1124begin
1125  // Defines an action for the dialog and creates it
1126  Action := GTK_FILE_CHOOSER_ACTION_OPEN;
1127  Button1 := GTK_STOCK_OPEN;
1128
1129  if (FileDialog is TSaveDialog) or (FileDialog is TSavePictureDialog) then
1130  begin
1131    Action := GTK_FILE_CHOOSER_ACTION_SAVE;
1132    Button1 := GTK_STOCK_SAVE;
1133  end
1134  else
1135  if FileDialog is TSelectDirectoryDialog then
1136  begin
1137    Action := GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
1138    Button1 := GTK_STOCK_OPEN;
1139  end;
1140
1141  Widget := gtk_file_chooser_dialog_new(PChar(FileDialog.Title), nil, Action,
1142    PChar(GTK_STOCK_CANCEL), [GTK_RESPONSE_CANCEL, PChar(Button1), GTK_RESPONSE_OK, nil]);
1143
1144  {$ifdef GTK_2_8}
1145  if FileDialog is TSaveDialog then
1146  begin
1147    gtk_file_chooser_set_do_overwrite_confirmation(Widget,
1148      ofOverwritePrompt in TOpenDialog(FileDialog).Options);
1149  end;
1150  {$endif}
1151
1152  if FileDialog.InitialDir <> '' then begin
1153    aSysFilename:=UTF8ToSys(FileDialog.InitialDir);
1154    gtk_file_chooser_set_current_folder(Widget, Pgchar(aSysFilename));
1155  end;
1156
1157  if gtk_file_chooser_get_action(Widget) in
1158    [GTK_FILE_CHOOSER_ACTION_SAVE, GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER]
1159  then
1160    gtk_file_chooser_set_current_name(Widget, Pgchar(FileDialog.FileName));
1161
1162  Result := THandle({%H-}PtrUInt(Widget));
1163  WidgetInfo := CreateWidgetInfo(Widget);
1164  WidgetInfo^.LCLObject := ACommonDialog;
1165  TGtk2WSCommonDialog.SetSizes(Widget, WidgetInfo);
1166  SetCallbacks(Widget, WidgetInfo);
1167end;
1168
1169{ TGtk2WSCommonDialog }
1170
1171{------------------------------------------------------------------------------
1172  Method: SetColorDialogColor
1173  Params:  ColorSelection : a gtk color selection dialog;
1174           Color          : the color to select
1175  Returns: nothing
1176
1177  Set the color of the color selection dialog
1178 ------------------------------------------------------------------------------}
1179class procedure TGtk2WSCommonDialog.SetColorDialogColor(ColorSelection: PGtkColorSelectionDialog;
1180  Color: TColor);
1181var
1182  SelectionColor: TGDKColor;
1183  colorSel: PGtkColorSelection;
1184begin
1185  Color := TColor(ColorToRGB(Color));
1186  SelectionColor := TColortoTGDKColor(Color);
1187  colorSel := PGtkColorSelection(ColorSelection^.colorsel);
1188  gtk_color_selection_set_current_color(colorSel, @SelectionColor);
1189  gtk_color_selection_set_previous_color(colorSel, @SelectionColor);
1190end;
1191
1192class procedure TGtk2WSCommonDialog.SetColorDialogPalette(
1193  ColorSelection: PGtkColorSelectionDialog; Palette: TStrings);
1194const
1195  PaletteSetting = 'gtk-color-palette';
1196var
1197  colorSel: PGtkColorSelection;
1198  settings: PGtkSettings;
1199  new_palette: Pgchar;
1200  colors: PGdkColor;
1201  colors_len: gint;
1202
1203  procedure FillCustomColors;
1204  var
1205    i, AIndex: integer;
1206    AColor: TColor;
1207  begin
1208    for i := 0 to Palette.Count - 1 do
1209      if ExtractColorIndexAndColor(Palette, i, AIndex, AColor) then
1210        if AIndex < colors_len then
1211          colors[AIndex] := TColortoTGDKColor(AColor);
1212  end;
1213
1214begin
1215  colorSel := PGtkColorSelection(ColorSelection^.colorsel);
1216  // show palette
1217  gtk_color_selection_set_has_palette(colorSel, True);
1218
1219  // replace palette. it is stored in 'gtk-color-palette' settings.
1220  // 1. get original palette => we will know colors and replace only part of it
1221  settings := gtk_widget_get_settings(PGtkWidget(colorSel));
1222  new_palette := nil;
1223  g_object_get(settings, PaletteSetting, [@new_palette, nil]);
1224  colors:=nil;
1225  gtk_color_selection_palette_from_string(new_palette, colors, @colors_len);
1226  g_free(new_palette);
1227
1228  // 2. fill original palette with our custom colors
1229  FillCustomColors;
1230
1231  // 3. set new palette back to settings
1232  new_palette := gtk_color_selection_palette_to_string(colors, colors_len);
1233  g_free(colors);
1234  gtk_settings_set_string_property(settings, PaletteSetting, new_palette, 'gtk_color_selection_palette_to_string');
1235  g_free(new_palette);
1236end;
1237
1238class procedure TGtk2WSCommonDialog.SetCallbacks(const AGtkWidget: PGtkWidget;
1239  const AWidgetInfo: PWidgetInfo);
1240begin
1241  g_signal_connect(PGtkObject(AGtkWidget),
1242    'destroy', gtk_Signal_Func(@gtkDialogDestroyCB), AWidgetInfo^.LCLObject);
1243  g_signal_connect(PGtkObject(AGtkWidget),
1244    'delete-event', gtk_Signal_Func(@gtkDialogCloseQueryCB), AWidgetInfo^.LCLObject);
1245  g_signal_connect(PGtkObject(AGtkWidget),
1246    'key-press-event', gtk_Signal_Func(@GTKDialogKeyUpDownCB), AWidgetInfo^.LCLObject);
1247  g_signal_connect(PGtkObject(AGtkWidget),
1248    'key-release-event', gtk_Signal_Func(@GTKDialogKeyUpDownCB), AWidgetInfo^.LCLObject);
1249  g_signal_connect(PGtkObject(AGtkWidget),
1250    'realize', gtk_Signal_Func(@GTKDialogRealizeCB), AWidgetInfo^.LCLObject);
1251end;
1252
1253class procedure TGtk2WSCommonDialog.SetSizes(const AGtkWidget: PGtkWidget;
1254  const AWidgetInfo: PWidgetInfo);
1255var
1256  NewWidth, NewHeight: integer;
1257begin
1258  // set default size
1259  NewWidth := TCommonDialog(AWidgetInfo^.LCLObject).Width;
1260  if NewWidth <= 0 then
1261    NewWidth := -2; // -2 = let the window manager decide
1262  NewHeight := TCommonDialog(AWidgetInfo^.LCLObject).Height;
1263  if NewHeight<=0 then
1264    NewHeight := -2; // -2 = let the window manager decide
1265  if (NewWidth > 0) or (NewHeight > 0) then
1266    gtk_window_set_default_size(PGtkWindow(AGtkWidget), NewWidth, NewHeight);
1267end;
1268
1269class function TGtk2WSCommonDialog.CreateHandle(
1270  const ACommonDialog: TCommonDialog): THandle;
1271begin
1272  Result := 0;
1273end;
1274
1275class procedure TGtk2WSCommonDialog.ShowModal(const ACommonDialog: TCommonDialog);
1276var
1277  GtkWindow: PGtkWindow;
1278begin
1279  ReleaseMouseCapture;
1280  GtkWindow:={%H-}PGtkWindow(ACommonDialog.Handle);
1281  gtk_window_set_title(GtkWindow,PChar(ACommonDialog.Title));
1282  if ACommonDialog is TColorDialog then
1283  begin
1284    SetColorDialogColor(PGtkColorSelectionDialog(GtkWindow),
1285                        TColorDialog(ACommonDialog).Color);
1286    SetColorDialogPalette(PGtkColorSelectionDialog(GtkWindow),
1287      TColorDialog(ACommonDialog).CustomColors);
1288  end;
1289
1290  gtk_window_set_position(GtkWindow, GTK_WIN_POS_CENTER);
1291  GtkWindowShowModal(nil, GtkWindow);
1292end;
1293
1294class procedure TGtk2WSCommonDialog.DestroyHandle(
1295  const ACommonDialog: TCommonDialog);
1296begin
1297  { TODO: cleanup }
1298  TGtk2WidgetSet(WidgetSet).DestroyLCLComponent(ACommonDialog);
1299end;
1300
1301{ TGtk2WSColorDialog }
1302
1303class procedure TGtk2WSColorDialog.SetCallbacks(const AGtkWidget: PGtkWidget;
1304  const AWidgetInfo: PWidgetInfo);
1305begin
1306  TGtk2WSCommonDialog.SetCallbacks(AGtkWidget, AWidgetInfo);
1307  g_signal_connect(PGtkObject(PGtkColorSelectionDialog(AGtkWidget)^.ok_button),
1308    'clicked', gtk_signal_func(@gtkDialogOKclickedCB), AWidgetInfo^.LCLObject);
1309  g_signal_connect(PGtkObject(PGtkColorSelectionDialog(AGtkWidget)^.cancel_button),
1310    'clicked', gtk_signal_func(@gtkDialogCancelclickedCB), AWidgetInfo^.LCLObject);
1311end;
1312
1313class function TGtk2WSColorDialog.CreateHandle(
1314  const ACommonDialog: TCommonDialog): THandle;
1315var
1316  Widget: PGtkWidget;
1317  WidgetInfo: PWidgetInfo;
1318begin
1319  Widget := gtk_color_selection_dialog_new(PChar(ACommonDialog.Title));
1320
1321  Result := THandle({%H-}PtrUInt(Widget));
1322  WidgetInfo := CreateWidgetInfo(Widget);
1323  WidgetInfo^.LCLObject := ACommonDialog;
1324  TGtk2WSCommonDialog.SetSizes(Widget, WidgetInfo);
1325  SetCallbacks(Widget, WidgetInfo);
1326end;
1327
1328class function TGtk2WSColorDialog.QueryWSEventCapabilities(
1329  const ACommonDialog: TCommonDialog): TCDWSEventCapabilities;
1330begin
1331  Result := [cdecWSPerformsDoShow];
1332end;
1333
1334{ TGtk2WSFontDialog }
1335
1336class procedure TGtk2WSFontDialog.SetCallbacks(const AGtkWidget: PGtkWidget;
1337  const AWidgetInfo: PWidgetInfo);
1338begin
1339  TGtk2WSCommonDialog.SetCallbacks(AGtkWidget, AWidgetInfo);
1340  // connect Ok, Cancel and Apply Button
1341  g_signal_connect(
1342    PGtkObject(PGtkFontSelectionDialog(AGtkWidget)^.ok_button),
1343    'clicked', gtk_signal_func(@gtkDialogOKclickedCB), AWidgetInfo^.LCLObject);
1344  g_signal_connect(
1345    PGtkObject(PGtkFontSelectionDialog(AGtkWidget)^.cancel_button),
1346    'clicked', gtk_signal_func(@gtkDialogCancelclickedCB), AWidgetInfo^.LCLObject);
1347  g_signal_connect(
1348    PGtkObject(PGtkFontSelectionDialog(AGtkWidget)^.apply_button),
1349    'clicked', gtk_signal_func(@gtkDialogApplyclickedCB), AWidgetInfo^.LCLObject);
1350end;
1351
1352class function TGtk2WSFontDialog.CreateHandle(const ACommonDialog: TCommonDialog): THandle;
1353var
1354  FontDesc: PPangoFontDescription;
1355  TmpStr: pChar;
1356
1357  Widget: PGtkWidget;
1358  WidgetInfo: PWidgetInfo;
1359  FontDialog: TFontDialog absolute ACommonDialog;
1360begin
1361  Widget := gtk_font_selection_dialog_new(PChar(ACommonDialog.Title));
1362
1363  if fdApplyButton in FontDialog.Options then
1364    gtk_widget_show(PGtkFontSelectionDialog(Widget)^.apply_button);
1365  // set preview text
1366  if FontDialog.PreviewText <> '' then
1367    gtk_font_selection_dialog_set_preview_text(PGtkFontSelectionDialog(Widget),
1368      PChar(FontDialog.PreviewText));
1369
1370  // set font name in XLFD format
1371  if IsFontNameXLogicalFontDesc(FontDialog.Font.Name) then
1372    gtk_font_selection_dialog_set_font_name(PGtkFontSelectionDialog(Widget),
1373      PChar(FontDialog.Font.Name))
1374  else
1375  begin
1376    FontDesc := pango_font_description_new;
1377    with FontDialog.Font do
1378    begin
1379      pango_font_description_set_size(FontDesc, Size * PANGO_SCALE);
1380
1381      if fsBold in Style then
1382        pango_font_description_set_weight(FontDesc, PANGO_WEIGHT_BOLD)
1383      else
1384        pango_font_description_set_weight(FontDesc, PANGO_WEIGHT_NORMAL);
1385
1386      if fsItalic in Style then
1387        pango_font_description_set_style(FontDesc, PANGO_STYLE_ITALIC)
1388      else
1389        pango_font_description_set_style(FontDesc, PANGO_STYLE_NORMAL);
1390
1391      pango_font_description_set_family(FontDesc, PChar(Name));
1392    end;
1393    TmpStr := pango_font_description_to_string(FontDesc);
1394    gtk_font_selection_dialog_set_font_name(PGtkFontSelectionDialog(Widget), TmpStr);
1395    g_free(TmpStr);
1396    pango_font_description_free(FontDesc);
1397  end;
1398
1399  { This functionality does not seem to be available in GTK2 }
1400  // Honor selected TFontDialogOption flags
1401
1402  Result := THandle({%H-}PtrUInt(Widget));
1403  WidgetInfo := CreateWidgetInfo(Widget);
1404  WidgetInfo^.LCLObject := ACommonDialog;
1405  TGtk2WSCommonDialog.SetSizes(Widget, WidgetInfo);
1406  SetCallbacks(Widget, WidgetInfo);
1407end;
1408
1409class function TGtk2WSFontDialog.QueryWSEventCapabilities(
1410  const ACommonDialog: TCommonDialog): TCDWSEventCapabilities;
1411begin
1412  Result := [cdecWSPerformsDoShow];
1413end;
1414
1415end.
1416