1 /* make and manage base windows ... dialog (messagebox, file box), top
2 * level windows
3 */
4
5 /*
6
7 Copyright (C) 1991-2001, The National Gallery
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License along
20 with this program; if not, write to the Free Software Foundation, Inc.,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22
23 */
24
25 /*
26
27 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
28
29 */
30
31 /*
32
33 build interface:
34
35 iwnd = iwindow_new( type );
36 iwindow_set_*( iwnd, ... );
37 iwindow_build( iwnd );
38
39 destroy interface:
40
41 iwindow_kill()
42
43 'cancellable' kill ... user popdown can return IWINDOW_ERROR
44 or IWINDOW_NO to prevent popdown
45
46 gtk_widget_destroy()
47
48 non-cancellable ... popdown is not called
49
50 so ... don't free() in popdown, subclass iwnd and free() in _destroy()
51
52 */
53
54 /*
55 #define DEBUG
56 */
57
58 #include "ip.h"
59
60 /* Cursor bitmaps.
61 */
62 #include "BITMAPS/dropper_src.xbm"
63 #include "BITMAPS/dropper_msk.xbm"
64 #include "BITMAPS/magin_src.xbm"
65 #include "BITMAPS/magout_src.xbm"
66 #include "BITMAPS/mag_msk.xbm"
67 #include "BITMAPS/watch_1.xbm"
68 #include "BITMAPS/watch_2.xbm"
69 #include "BITMAPS/watch_3.xbm"
70 #include "BITMAPS/watch_4.xbm"
71 #include "BITMAPS/watch_5.xbm"
72 #include "BITMAPS/watch_6.xbm"
73 #include "BITMAPS/watch_7.xbm"
74 #include "BITMAPS/watch_8.xbm"
75 #include "BITMAPS/watch_msk.xbm"
76
77 static GtkWindowClass *parent_class = NULL;
78
79 /* List of all iwindows.
80 */
81 static GSList *iwindow_all = NULL;
82
83 /* All our cursors.
84 */
85 static GdkCursor *iwindow_cursor[IWINDOW_SHAPE_LAST] = { NULL };
86
87 #ifdef DEBUG
88 /* Human-readable names for cursor shapes.
89 */
90 static const char *iwindow_cursor_name[] = {
91 "IWINDOW_SHAPE_DROPPER",
92 "IWINDOW_SHAPE_PEN",
93 "IWINDOW_SHAPE_SMUDGE",
94 "IWINDOW_SHAPE_SMEAR",
95 "IWINDOW_SHAPE_TEXT",
96 "IWINDOW_SHAPE_RECT",
97 "IWINDOW_SHAPE_FLOOD",
98 "IWINDOW_SHAPE_MOVE",
99 "IWINDOW_SHAPE_EDIT",
100 "IWINDOW_SHAPE_MAGIN",
101 "IWINDOW_SHAPE_MAGOUT",
102 "IWINDOW_SHAPE_TOP",
103 "IWINDOW_SHAPE_BOTTOM",
104 "IWINDOW_SHAPE_LEFT",
105 "IWINDOW_SHAPE_RIGHT",
106 "IWINDOW_SHAPE_TOPRIGHT",
107 "IWINDOW_SHAPE_TOPLEFT",
108 "IWINDOW_SHAPE_BOTTOMRIGHT",
109 "IWINDOW_SHAPE_BOTTOMLEFT",
110 "IWINDOW_SHAPE_HGLASS1",
111 "IWINDOW_SHAPE_HGLASS2",
112 "IWINDOW_SHAPE_HGLASS3",
113 "IWINDOW_SHAPE_HGLASS4",
114 "IWINDOW_SHAPE_HGLASS5",
115 "IWINDOW_SHAPE_HGLASS6",
116 "IWINDOW_SHAPE_HGLASS7",
117 "IWINDOW_SHAPE_HGLASS8",
118 "IWINDOW_SHAPE_NONE"
119 };
120 #endif /*DEBUG*/
121
122 int
iwindow_number(void)123 iwindow_number( void )
124 {
125 return( g_slist_length( iwindow_all ) );
126 }
127
128 /* Pick an iwindow at random. Used if we need a window for a dialog, and we're
129 * not sure which to pick. During shutdown we can have no windows.
130 */
131 iWindow *
iwindow_pick_one(void)132 iwindow_pick_one( void )
133 {
134 if( !iwindow_all )
135 return( NULL );
136
137 return( IWINDOW( iwindow_all->data ) );
138 }
139
140 /* Over all windows.
141 */
142 void *
iwindow_map_all(iWindowMapFn fn,void * a)143 iwindow_map_all( iWindowMapFn fn, void *a )
144 {
145 return( slist_map( iwindow_all, (SListMapFn) fn, a ) );
146 }
147
148 /* Make a custom cursor ... source, mask, width, height and hot spot position.
149 */
150 static GdkCursor *
iwindow_make_cursor_data(guchar * src_bits,guchar * msk_bits,int w,int h,int x,int y)151 iwindow_make_cursor_data( guchar *src_bits, guchar *msk_bits,
152 int w, int h, int x, int y )
153 {
154 GdkPixmap *src;
155 GdkPixmap *msk;
156 GdkCursor *cursor;
157 GdkColor fg = { 0, 255 << 8, 255 << 8, 255 << 8 };
158 GdkColor bg = { 0, 0, 0, 0 };
159
160 src = gdk_bitmap_create_from_data( NULL,
161 (const char *) src_bits, w, h );
162 msk = gdk_bitmap_create_from_data( NULL,
163 (const char *) msk_bits, w, h );
164 cursor = gdk_cursor_new_from_pixmap( src, msk, &fg, &bg, x, y );
165 gdk_pixmap_unref( src );
166 gdk_pixmap_unref( msk );
167
168 return( cursor );
169 }
170
171 /* Build all the cursors.
172 */
173 static void
iwindow_make_cursors(void)174 iwindow_make_cursors( void )
175 {
176 /* Init standard cursors with this table.
177 */
178 static GdkCursorType standards[] = {
179 GDK_CURSOR_IS_PIXMAP, /* IWINDOW_SHAPE_DROPPER */
180 GDK_PENCIL, /* IWINDOW_SHAPE_PEN */
181 GDK_HAND2, /* IWINDOW_SHAPE_SMUDGE */
182 GDK_SPIDER, /* IWINDOW_SHAPE_SMEAR */
183 GDK_GOBBLER, /* IWINDOW_SHAPE_TEXT */
184 GDK_SIZING, /* IWINDOW_SHAPE_RECT */
185 GDK_TREK, /* IWINDOW_SHAPE_FLOOD */
186 GDK_FLEUR, /* IWINDOW_SHAPE_MOVE */
187 GDK_CROSSHAIR, /* IWINDOW_SHAPE_EDIT */
188 GDK_CURSOR_IS_PIXMAP, /* IWINDOW_SHAPE_MAGIN */
189 GDK_CURSOR_IS_PIXMAP, /* IWINDOW_SHAPE_MAGOUT */
190 GDK_TOP_SIDE, /* IWINDOW_SHAPE_TOP */
191 GDK_BOTTOM_SIDE, /* IWINDOW_SHAPE_BOTTOM */
192 GDK_LEFT_SIDE, /* IWINDOW_SHAPE_LEFT */
193 GDK_RIGHT_SIDE, /* IWINDOW_SHAPE_RIGHT */
194 GDK_TOP_RIGHT_CORNER, /* IWINDOW_SHAPE_TOPRIGHT */
195 GDK_TOP_LEFT_CORNER, /* IWINDOW_SHAPE_TOPLEFT */
196 GDK_BOTTOM_RIGHT_CORNER,/* IWINDOW_SHAPE_BOTTOMRIGHT, */
197 GDK_BOTTOM_LEFT_CORNER, /* IWINDOW_SHAPE_BOTTOMLEFT */
198 };
199
200 /* All the bits for the rotating cursor.
201 */
202 static guchar *watch_bits[] = {
203 watch_1_bits,
204 watch_2_bits,
205 watch_3_bits,
206 watch_4_bits,
207 watch_5_bits,
208 watch_6_bits,
209 watch_7_bits,
210 watch_8_bits,
211 };
212
213 int i;
214
215 if( iwindow_cursor[0] )
216 return;
217
218 /* Easy ones first.
219 */
220 for( i = 0; i < IM_NUMBER( standards ); i++ )
221 if( standards[i] != GDK_CURSOR_IS_PIXMAP )
222 iwindow_cursor[i] = gdk_cursor_new( standards[i] );
223
224 /* Custom cursors.
225 */
226 iwindow_cursor[IWINDOW_SHAPE_DROPPER] = iwindow_make_cursor_data(
227 dropper_src_bits, dropper_msk_bits,
228 dropper_src_width, dropper_src_height, 0, 15 );
229 iwindow_cursor[IWINDOW_SHAPE_MAGIN] = iwindow_make_cursor_data(
230 magin_src_bits, mag_msk_bits,
231 mag_msk_width, mag_msk_height, 6, 6 );
232 iwindow_cursor[IWINDOW_SHAPE_MAGOUT] = iwindow_make_cursor_data(
233 magout_src_bits, mag_msk_bits,
234 mag_msk_width, mag_msk_height, 6, 6 );
235
236 /* The hglasses.
237 */
238 for( i = 0; i < IM_NUMBER( watch_bits ); i++ )
239 iwindow_cursor[IWINDOW_SHAPE_HGLASS1 + i] =
240 iwindow_make_cursor_data(
241 watch_bits[i], watch_msk_bits,
242 watch_1_width, watch_1_height, 7, 7 );
243 }
244
245 /* Get the work window.
246 */
247 static GdkWindow *
iwindow_get_work_window(iWindow * iwnd)248 iwindow_get_work_window( iWindow *iwnd )
249 {
250 if( iwnd->work_window )
251 return( iwnd->work_window );
252 else
253 return( GTK_WIDGET( iwnd )->window );
254 }
255
256 /* Update the cursor for a window.
257 */
258 static void *
iwindow_cursor_update(iWindow * iwnd)259 iwindow_cursor_update( iWindow *iwnd )
260 {
261 if( GTK_WIDGET_REALIZED( GTK_WIDGET( iwnd ) ) ) {
262 GSList *p;
263 iWindowShape best_shape;
264 int best_priority;
265
266 /* Global shape set? Use that for the whole window.
267 */
268 if( iwnd->shape != IWINDOW_SHAPE_NONE ) {
269 gdk_window_set_cursor( GTK_WIDGET( iwnd )->window,
270 iwindow_cursor[iwnd->shape] );
271 gdk_window_set_cursor( iwindow_get_work_window( iwnd ),
272 iwindow_cursor[iwnd->shape] );
273 gdk_flush();
274
275 return( NULL );
276 }
277
278 /* No global shape ... make sure there's no global cursor on
279 * this window.
280 */
281 gdk_window_set_cursor( GTK_WIDGET( iwnd )->window, NULL );
282 gdk_window_set_cursor( iwindow_get_work_window( iwnd ), NULL );
283
284 /* And set the work area to the highest priority non-NONE
285 * shape we can find .
286
287 FIXME ... could avoid the search if we sorted the
288 context list by priority on each context_new(),
289 but not very important.
290
291 */
292 best_shape = IWINDOW_SHAPE_NONE;
293 best_priority = -1;
294 for( p = iwnd->contexts; p; p = p->next ) {
295 iWindowCursorContext *cntxt =
296 (iWindowCursorContext *) p->data;
297
298 if( cntxt->shape != IWINDOW_SHAPE_NONE &&
299 cntxt->priority > best_priority ) {
300 best_shape = cntxt->shape;
301 best_priority = cntxt->priority;
302 }
303 }
304
305 /* Pref to disable crosshair.
306 */
307 if( best_shape == IWINDOW_SHAPE_EDIT && !DISPLAY_CROSSHAIR )
308 best_shape = IWINDOW_SHAPE_NONE;
309
310 gdk_window_set_cursor(
311 iwindow_get_work_window( iwnd ),
312 iwindow_cursor[best_shape] );
313 gdk_flush();
314 }
315
316 return( NULL );
317 }
318
319 /* Set a global cursor for a window.
320 */
321 static void *
iwindow_cursor_set(iWindow * iwnd,iWindowShape * shape)322 iwindow_cursor_set( iWindow *iwnd, iWindowShape *shape )
323 {
324 if( iwnd->shape != *shape ) {
325 iwnd->shape = *shape;
326 iwindow_cursor_update( iwnd );
327 }
328
329 return( NULL );
330 }
331
332 static gboolean hourglass_showing = FALSE;
333
334 static void
hourglass_begin(void)335 hourglass_begin( void )
336 {
337 hourglass_showing = TRUE;
338 }
339
340 static void
hourglass_update(void)341 hourglass_update( void )
342 {
343 if( hourglass_showing ) {
344 static iWindowShape shape = IWINDOW_SHAPE_HGLASS1;
345
346 iwindow_map_all( (iWindowMapFn) iwindow_cursor_set, &shape );
347
348 shape += 1;
349 if( shape > IWINDOW_SHAPE_HGLASS8 )
350 shape = IWINDOW_SHAPE_HGLASS1;
351 }
352 }
353
354 static void
hourglass_end(void)355 hourglass_end( void )
356 {
357 if( hourglass_showing ) {
358 iWindowShape shape = IWINDOW_SHAPE_NONE;
359
360 iwindow_map_all( (iWindowMapFn) iwindow_cursor_set, &shape );
361 hourglass_showing = FALSE;
362 }
363 }
364
365 iWindowCursorContext *
iwindow_cursor_context_new(iWindow * iwnd,int priority,const char * name)366 iwindow_cursor_context_new( iWindow *iwnd, int priority, const char *name )
367 {
368 iWindowCursorContext *cntxt = INEW( NULL, iWindowCursorContext );
369
370 #ifdef DEBUG
371 printf( "iwindow_cursor_context_new: %s\n", name );
372 #endif /*DEBUG*/
373
374 cntxt->iwnd = iwnd;
375 cntxt->priority = priority;
376 cntxt->name = name;
377 cntxt->shape = IWINDOW_SHAPE_NONE;
378 iwnd->contexts = g_slist_prepend( iwnd->contexts, cntxt );
379
380 return( cntxt );
381 }
382
383 void
iwindow_cursor_context_destroy(iWindowCursorContext * cntxt)384 iwindow_cursor_context_destroy( iWindowCursorContext *cntxt )
385 {
386 iWindow *iwnd = cntxt->iwnd;
387
388 iwnd->contexts = g_slist_remove( iwnd->contexts, cntxt );
389 IM_FREE( cntxt );
390 iwindow_cursor_update( iwnd );
391 }
392
393 void
iwindow_cursor_context_set_cursor(iWindowCursorContext * cntxt,iWindowShape shape)394 iwindow_cursor_context_set_cursor( iWindowCursorContext *cntxt,
395 iWindowShape shape )
396 {
397 if( cntxt->shape != shape ) {
398 #ifdef DEBUG
399 printf( "iwindow_cursor_context_set_cursor: %s = %s\n",
400 cntxt->name, iwindow_cursor_name[shape] );
401 #endif /*DEBUG*/
402
403 cntxt->shape = shape;
404 iwindow_cursor_update( cntxt->iwnd );
405 }
406 }
407
408 iWindowSusp *
iwindow_susp_new(iWindowFn fn,iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)409 iwindow_susp_new( iWindowFn fn,
410 iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys )
411 {
412 iWindowSusp *susp;
413
414 if( !(susp = INEW( NULL, iWindowSusp )) )
415 return( NULL );
416
417 susp->fn = fn;
418 susp->iwnd = iwnd;
419 susp->client = client;
420 susp->nfn = nfn;
421 susp->sys = sys;
422
423 return( susp );
424 }
425
426 /* Trigger a suspension's reply, and free it.
427 */
428 void
iwindow_susp_return(void * sys,iWindowResult result)429 iwindow_susp_return( void *sys, iWindowResult result )
430 {
431 iWindowSusp *susp = IWINDOW_SUSP( sys );
432
433 susp->nfn( susp->sys, result );
434
435 im_free( susp );
436 }
437
438 void
iwindow_susp_trigger(iWindowSusp * susp)439 iwindow_susp_trigger( iWindowSusp *susp )
440 {
441 susp->fn( susp->iwnd, susp->client, susp->nfn, susp->sys );
442 im_free( susp );
443 }
444
445 /* Compose two iWindowFns ... if this one succeeded, trigger the next in turn.
446 * Otherwise bail out.
447 */
448 void
iwindow_susp_comp(void * sys,iWindowResult result)449 iwindow_susp_comp( void *sys, iWindowResult result )
450 {
451 iWindowSusp *susp = IWINDOW_SUSP( sys );
452
453 if( result == IWINDOW_YES )
454 iwindow_susp_trigger( susp );
455 else
456 iwindow_susp_return( sys, result );
457 }
458
459 /* Null window callback.
460 */
461 void
iwindow_true_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)462 iwindow_true_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys )
463 {
464 nfn( sys, IWINDOW_YES );
465 }
466
467 void
iwindow_false_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)468 iwindow_false_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys )
469 {
470 nfn( sys, IWINDOW_NO );
471 }
472
473 /* Null notify callback.
474 */
iwindow_notify_null(void * client,iWindowResult result)475 void iwindow_notify_null( void *client, iWindowResult result ) { }
476
477 /* Final end of a window. Destroy!
478 */
479 static void
iwindow_final_death(iWindow * iwnd)480 iwindow_final_death( iWindow *iwnd )
481 {
482 #ifdef DEBUG
483 printf( "iwindow_final_death: %s\n", iwnd->title );
484 #endif /*DEBUG*/
485
486 g_assert( iwnd->pending == 0 && iwnd->destroy );
487
488 /* Clean up.
489 */
490 gtk_widget_destroy( GTK_WIDGET( iwnd ) );
491 }
492
493 /* A notify comes back ... adjust the pending count. If this is a zombie and
494 * this is the final pending, it's final death.
495 */
496 void
iwindow_notify_return(iWindow * iwnd)497 iwindow_notify_return( iWindow *iwnd )
498 {
499 #ifdef DEBUG
500 printf( "iwindow_notify_return: %s (pending = %d)\n",
501 iwnd->title, iwnd->pending );
502 #endif /*DEBUG*/
503
504 g_assert( iwnd->pending > 0 );
505
506 iwnd->pending--;
507 if( iwnd->destroy && iwnd->pending == 0 ) {
508 #ifdef DEBUG
509 printf( "iwindow_notify_return: zombie death %s\n",
510 iwnd->title );
511 #endif /*DEBUG*/
512 iwindow_final_death( iwnd );
513 }
514 }
515
516 /* Send a notify off, tell the client to come back to back.
517 */
518 void
iwindow_notify_send(iWindow * iwnd,iWindowFn fn,void * client,iWindowNotifyFn back,void * sys)519 iwindow_notify_send( iWindow *iwnd,
520 iWindowFn fn, void *client, iWindowNotifyFn back, void *sys )
521 {
522 #ifdef DEBUG
523 printf( "iwindow_notify_send: %s (pending = %d)\n",
524 iwnd->title, iwnd->pending );
525 #endif /*DEBUG*/
526
527 iwnd->pending++;
528 if( fn )
529 fn( iwnd, client, back, sys );
530 else
531 back( sys, IWINDOW_YES );
532 }
533
534 static void
iwindow_finalize(GObject * gobject)535 iwindow_finalize( GObject *gobject )
536 {
537 iWindow *iwnd = IWINDOW( gobject );
538
539 #ifdef DEBUG
540 printf( "iwindow_finalize: %s\n", iwnd->title );
541 #endif /*DEBUG*/
542
543 /* My instance destroy stuff.
544 */
545 iwindow_all = g_slist_remove( iwindow_all, iwnd );
546 IM_FREE( iwnd->title );
547
548 G_OBJECT_CLASS( parent_class )->finalize( gobject );
549
550 /* Last window and we've got through startup? Quit the application.
551 */
552 if( iwindow_number() == 0 &&
553 !main_starting )
554 main_quit_test();
555 }
556
557 static void
iwindow_destroy(GtkObject * gobject)558 iwindow_destroy( GtkObject *gobject )
559 {
560 iWindow *iwnd = IWINDOW( gobject );
561
562 #ifdef DEBUG
563 printf( "iwindow_destroy: %s\n", iwnd->title );
564 #endif /*DEBUG*/
565
566 /* My instance destroy stuff.
567 */
568 FREESID( iwnd->parent_unmap_sid, iwnd->parent_window );
569 UNREF( iwnd->action_group );
570 UNREF( iwnd->ui_manager );
571
572 /* Now we've destroyed, we must stop popdown from being called, since
573 * the view will have junked a lot of stuff.
574 */
575 iwnd->destroy = TRUE;
576
577 GTK_OBJECT_CLASS( parent_class )->destroy( gobject );
578 }
579
580 static void
iwindow_popdown_notify(iWindow * iwnd,iWindowResult result)581 iwindow_popdown_notify( iWindow *iwnd, iWindowResult result )
582 {
583 #ifdef DEBUG
584 printf( "iwindow_popdown_notify: %p %s\n", iwnd, iwnd->title );
585 #endif /*DEBUG*/
586
587 if( result == IWINDOW_ERROR )
588 iwindow_alert( GTK_WIDGET( iwnd ), GTK_MESSAGE_ERROR );
589 else if( result == IWINDOW_YES )
590 iwindow_kill( iwnd );
591
592 if( result != IWINDOW_YES ) {
593 #ifdef DEBUG
594 printf( "iwindow_popdown_notify: %s: kill cancelled!\n",
595 iwnd->title );
596 #endif /*DEBUG*/
597
598 /* Cancel popdown.
599 */
600 iwnd->destroy = FALSE;
601 }
602 else {
603 /* Popdown confirmed! Trigger class popdown. _real_popdown()
604 * does an unmap to hide the window during the rest of the
605 * destroy.
606 */
607 IWINDOW_GET_CLASS( iwnd )->popdown( GTK_WIDGET( iwnd ) );
608 }
609
610 calli_string_filenamef( (calli_string_fn) gtk_accel_map_save,
611 "%s" G_DIR_SEPARATOR_S "accel_map", get_savedir() );
612
613 /* If this is the final pending response and ->destroy is true, this
614 * will destroy the window.
615 */
616 iwindow_notify_return( iwnd );
617 }
618
619 static gboolean
iwindow_delete_event(GtkWidget * widget,GdkEventAny * event)620 iwindow_delete_event( GtkWidget *widget, GdkEventAny *event )
621 {
622 iWindow *iwnd = IWINDOW( widget );
623
624 #ifdef DEBUG
625 printf( "iwindow_delete_event: %s\n", iwnd->title );
626 #endif /*DEBUG*/
627
628 if( !iwnd->destroy ) {
629 #ifdef DEBUG
630 printf( "iwindow_delete_event: starting destroy\n" );
631 #endif /*DEBUG*/
632
633 iwindow_kill( iwnd );
634 }
635
636 /* Never delete here ... wait for iwindow_popdown_notify to
637 * confirm the kill.
638 */
639 return( TRUE );
640 }
641
642 static gboolean
iwindow_configure_event(GtkWidget * widget,GdkEventConfigure * event)643 iwindow_configure_event( GtkWidget *widget, GdkEventConfigure *event )
644 {
645 iWindow *iwnd = IWINDOW( widget );
646
647 if( iwnd->width_pref ) {
648 /* Save window size in global prefs.
649 */
650 prefs_set( iwnd->width_pref, "%d", event->width );
651 prefs_set( iwnd->height_pref, "%d", event->height );
652 }
653
654 return( GTK_WIDGET_CLASS( parent_class )->
655 configure_event( widget, event ) );
656 }
657
658 /* Our parent has been destroyed, kill us too.
659 */
660 static void
iwindow_parent_unmap_cb(GtkWidget * par,iWindow * iwnd)661 iwindow_parent_unmap_cb( GtkWidget *par, iWindow *iwnd )
662 {
663 #ifdef DEBUG
664 printf( "iwindow_parent_unmap_cb: %s\n", iwnd->title );
665 #endif /*DEBUG*/
666
667 /* Here for dead parent ... if parent is dead, we won't need to remove
668 * the dead-dad signal.
669 */
670 iwnd->parent_unmap_sid = 0;
671
672 iwindow_kill( iwnd );
673 }
674
675 static GtkActionEntry iwnd_actions[] = {
676 /* Common menus.
677 */
678 { "FileMenu", NULL, N_( "_File" ) },
679 { "NewMenu", NULL, N_( "_New" ) },
680 { "EditMenu", NULL, N_( "_Edit" ) },
681 { "ViewMenu", NULL, N_( "_View" ) },
682 { "HelpMenu", NULL, N_( "_Help" ) },
683
684 /* Common items.
685 */
686 { "Close",
687 GTK_STOCK_CLOSE, N_( "_Close" ), NULL,
688 N_( "Close" ),
689 G_CALLBACK( iwindow_kill_action_cb ) },
690
691 { "Quit",
692 GTK_STOCK_QUIT, N_( "_Quit" ), "<control>q",
693 N_( "Quit nip2" ),
694 G_CALLBACK( main_quit_test ) },
695 { "Guide",
696 GTK_STOCK_HELP, N_( "_Contents" ), "F1",
697 N_( "Open the users guide" ),
698 G_CALLBACK( mainw_guide_action_cb ) },
699
700 { "About",
701 NULL, N_( "_About" ), NULL,
702 N_( "About this program" ),
703 G_CALLBACK( mainw_about_action_cb ) },
704
705 { "Homepage",
706 NULL, N_( "_Website" ), NULL,
707 N_( "Open the VIPS Homepage" ),
708 G_CALLBACK( mainw_homepage_action_cb ) }
709 };
710
711 static void
iwindow_real_build(GtkWidget * widget)712 iwindow_real_build( GtkWidget *widget )
713 {
714 iWindow *iwnd = IWINDOW( widget );
715 GdkScreen *screen = gtk_widget_get_screen( GTK_WIDGET( iwnd ) );
716
717 GtkAccelGroup *accel_group;
718
719 #ifdef DEBUG
720 printf( "iwindow_real_build: %s\n", iwnd->title );
721 #endif /*DEBUG*/
722
723 gtk_container_set_border_width( GTK_CONTAINER( iwnd ), 0 );
724
725 iwnd->work = gtk_vbox_new( FALSE, 0 );
726 gtk_container_add( GTK_CONTAINER( iwnd ), iwnd->work );
727
728 /* Use the type name (eg. "Imageview") for the name of the
729 * actiongroup.
730 */
731 iwnd->action_group = gtk_action_group_new( G_OBJECT_TYPE_NAME( iwnd ) );
732 gtk_action_group_set_translation_domain( iwnd->action_group,
733 GETTEXT_PACKAGE );
734 gtk_action_group_add_actions( iwnd->action_group,
735 iwnd_actions, G_N_ELEMENTS( iwnd_actions ),
736 GTK_WINDOW( iwnd ) );
737
738 iwnd->ui_manager = gtk_ui_manager_new();
739 gtk_ui_manager_insert_action_group( iwnd->ui_manager,
740 iwnd->action_group, 0 );
741
742 accel_group = gtk_ui_manager_get_accel_group( iwnd->ui_manager );
743 gtk_window_add_accel_group( GTK_WINDOW( iwnd ), accel_group );
744
745 /* Call per-instance build.
746 */
747 if( iwnd->build )
748 iwnd->build( iwnd, iwnd->work,
749 iwnd->build_a, iwnd->build_b, iwnd->build_c );
750
751 if( iwnd->title )
752 gtk_window_set_title( GTK_WINDOW( iwnd ), iwnd->title );
753
754 if( iwnd->width_pref ) {
755 int width = watch_int_get( main_watchgroup,
756 iwnd->width_pref, 640 );
757 int height = watch_int_get( main_watchgroup,
758 iwnd->height_pref, 480 );
759
760 gtk_window_set_default_size( GTK_WINDOW( iwnd ),
761 IM_MIN( width, gdk_screen_get_width( screen ) ),
762 IM_MIN( height, gdk_screen_get_height( screen ) ) );
763 }
764
765 /* Link to parent.
766 */
767 if( iwnd->parent_window ) {
768 if( IWINDOW_GET_CLASS( iwnd )->transient &&
769 iwnd->parent_window &&
770 iwnd != iwnd->parent_window )
771 gtk_window_set_transient_for( GTK_WINDOW( iwnd ),
772 GTK_WINDOW( iwnd->parent_window ) );
773
774 /* We watch our parent's "unmap" rather than "destroy" since
775 * we use gtk_widget_unmap() to hide killed windows during
776 * popdown (see iwindow_popdown_notify()).
777 */
778 iwnd->parent_unmap_sid = gtk_signal_connect(
779 GTK_OBJECT( iwnd->parent_window ),
780 "unmap",
781 GTK_SIGNAL_FUNC( iwindow_parent_unmap_cb ), iwnd );
782
783 /* Show the parent. For example, if this is the ^Q
784 * save-or-quit dialog and the parent is a mainw, we want to
785 * pop the mainw up.
786 */
787 gtk_window_present( GTK_WINDOW( iwnd->parent_window ) );
788 }
789
790 gtk_widget_show( iwnd->work );
791 }
792
793 static void
iwindow_real_popdown(GtkWidget * widget)794 iwindow_real_popdown( GtkWidget *widget )
795 {
796 gtk_widget_unmap( widget );
797 }
798
799 static void
iwindow_class_init(iWindowClass * class)800 iwindow_class_init( iWindowClass *class )
801 {
802 GObjectClass *object_class = (GObjectClass *) class;
803 GtkObjectClass *gobject_class = (GtkObjectClass *) class;
804 GtkWidgetClass *widget_class = (GtkWidgetClass *) class;
805
806 parent_class = g_type_class_peek_parent( class );
807
808 /* Init methods.
809 */
810 object_class->finalize = iwindow_finalize;
811
812 gobject_class->destroy = iwindow_destroy;
813
814 widget_class->delete_event = iwindow_delete_event;
815 widget_class->configure_event = iwindow_configure_event;
816
817 class->build = iwindow_real_build;
818 class->popdown = iwindow_real_popdown;
819
820 class->transient = FALSE;
821
822 /* Create signals.
823 */
824
825 /* Static class data init.
826 */
827 iwindow_make_cursors();
828
829 /* Link to busy signals.
830 */
831 g_signal_connect( progress_get(), "begin", hourglass_begin, NULL );
832 g_signal_connect( progress_get(), "update", hourglass_update, NULL );
833 g_signal_connect( progress_get(), "end", hourglass_end, NULL );
834 }
835
836 static void
iwindow_init(iWindow * iwnd)837 iwindow_init( iWindow *iwnd )
838 {
839 #ifdef DEBUG
840 printf( "iwindow_init: %s\n", iwnd->title );
841 #endif /*DEBUG*/
842
843 iwnd->work = NULL;
844
845 iwnd->parent = NULL;
846 iwnd->parent_window = NULL;
847 iwnd->parent_unmap_sid = 0;
848
849 /* Might as well.
850 */
851 iwnd->accel_group = gtk_accel_group_new();
852 gtk_window_add_accel_group( GTK_WINDOW( iwnd ), iwnd->accel_group );
853 g_object_unref( iwnd->accel_group );
854 iwnd->infobar = NULL;
855
856 iwnd->title = NULL;
857
858 iwnd->build = NULL;
859 iwnd->popdown = iwindow_true_cb;
860
861 iwnd->destroy = FALSE;
862 iwnd->pending = 0;
863
864 iwnd->shape = IWINDOW_SHAPE_NONE;
865 iwnd->contexts = NULL;
866 iwnd->work_window = NULL;
867
868 iwnd->width_pref = NULL;
869 iwnd->height_pref = NULL;
870
871 iwindow_all = g_slist_prepend( iwindow_all, iwnd );
872 }
873
874 GtkType
iwindow_get_type(void)875 iwindow_get_type( void )
876 {
877 static GtkType type = 0;
878
879 if( !type ) {
880 static const GtkTypeInfo info = {
881 "iWindow",
882 sizeof( iWindow ),
883 sizeof( iWindowClass ),
884 (GtkClassInitFunc) iwindow_class_init,
885 (GtkObjectInitFunc) iwindow_init,
886 /* reserved_1 */ NULL,
887 /* reserved_2 */ NULL,
888 (GtkClassInitFunc) NULL,
889 };
890
891 type = gtk_type_unique( GTK_TYPE_WINDOW, &info );
892 }
893
894 return( type );
895 }
896
897 GtkWidget *
iwindow_new(GtkWindowType type)898 iwindow_new( GtkWindowType type )
899 {
900 iWindow *iwnd = gtk_type_new( TYPE_IWINDOW );
901 GtkWindow *gwnd = GTK_WINDOW( iwnd );
902
903 /* Init superclass.
904 */
905 gwnd->type = type;
906
907 return( GTK_WIDGET( iwnd ) );
908 }
909
910 void
iwindow_set_title(iWindow * iwnd,const char * title,...)911 iwindow_set_title( iWindow *iwnd, const char *title, ... )
912 {
913 va_list ap;
914 char buf[1024];
915
916 va_start( ap, title );
917 (void) im_vsnprintf( buf, 1024, title, ap );
918 va_end( ap );
919
920 if( !iwnd->title || strcmp( iwnd->title, buf ) != 0 ) {
921 IM_SETSTR( iwnd->title, buf );
922 gtk_window_set_title( GTK_WINDOW( iwnd ), iwnd->title );
923 }
924 }
925
926 void
iwindow_set_build(iWindow * iwnd,iWindowBuildFn build,void * build_a,void * build_b,void * build_c)927 iwindow_set_build( iWindow *iwnd,
928 iWindowBuildFn build, void *build_a, void *build_b, void *build_c )
929 {
930 iwnd->build = build;
931 iwnd->build_a = build_a;
932 iwnd->build_b = build_b;
933 iwnd->build_c = build_c;
934 }
935
936 void
iwindow_set_popdown(iWindow * iwnd,iWindowFn popdown,void * popdown_a)937 iwindow_set_popdown( iWindow *iwnd, iWindowFn popdown, void *popdown_a )
938 {
939 iwnd->popdown = popdown;
940 iwnd->popdown_a = popdown_a;
941 }
942
943 void
iwindow_set_size_prefs(iWindow * iwnd,const char * width_pref,const char * height_pref)944 iwindow_set_size_prefs( iWindow *iwnd,
945 const char *width_pref, const char *height_pref )
946 {
947 iwnd->width_pref = width_pref;
948 iwnd->height_pref = height_pref;
949 }
950
951 void
iwindow_set_work_window(iWindow * iwnd,GdkWindow * work_window)952 iwindow_set_work_window( iWindow *iwnd, GdkWindow *work_window )
953 {
954 iwnd->work_window = work_window;
955 iwindow_cursor_update( iwnd );
956 }
957
958 void
iwindow_set_parent(iWindow * iwnd,GtkWidget * parent)959 iwindow_set_parent( iWindow *iwnd, GtkWidget *parent )
960 {
961 g_assert( !iwnd->parent );
962
963 iwnd->parent = parent;
964
965 /* Get parent window now, we sometimes need it after parent has been
966 * destroyed.
967 */
968 if( parent )
969 iwnd->parent_window =
970 IWINDOW( iwindow_get_root( GTK_WIDGET( parent ) ) );
971 }
972
973 void *
iwindow_kill(iWindow * iwnd)974 iwindow_kill( iWindow *iwnd )
975 {
976 #ifdef DEBUG
977 printf( "iwindow_kill: %p %s\n", iwnd, iwnd->title );
978 #endif /*DEBUG*/
979
980 if( !iwnd->destroy ) {
981 #ifdef DEBUG
982 printf( "... starting destroy for %s\n", iwnd->title );
983 #endif /*DEBUG*/
984
985 iwnd->destroy = TRUE;
986
987 /* Don't kill directly, wait for popdown_notify to do it.
988 */
989 iwindow_notify_send( iwnd, iwnd->popdown, iwnd->popdown_a,
990 (iWindowNotifyFn) iwindow_popdown_notify, iwnd );
991 }
992
993 return( NULL );
994 }
995
996 /* ... as an action.
997 */
998 void
iwindow_kill_action_cb(GtkAction * action,iWindow * iwnd)999 iwindow_kill_action_cb( GtkAction *action, iWindow *iwnd )
1000 {
1001 iwindow_kill( iwnd );
1002 }
1003
1004 void
iwindow_build(iWindow * iwnd)1005 iwindow_build( iWindow *iwnd )
1006 {
1007 #ifdef DEBUG
1008 printf( "iwindow_build: %s\n", iwnd->title );
1009 #endif /*DEBUG*/
1010
1011 IWINDOW_GET_CLASS( iwnd )->build( GTK_WIDGET( iwnd ) );
1012 }
1013
1014 /* Get the enclosing window for a widget.
1015 */
1016 GtkWidget *
iwindow_get_root(GtkWidget * widget)1017 iwindow_get_root( GtkWidget *widget )
1018 {
1019 GtkWidget *toplevel = gtk_widget_get_toplevel( widget );
1020 GtkWidget *child = gtk_bin_get_child( GTK_BIN( toplevel ) );
1021
1022 /* If this is a menu pane, get the widget that popped this menu up.
1023 */
1024 if( GTK_IS_MENU( child ) ) {
1025 GtkWidget *parent =
1026 gtk_menu_get_attach_widget( GTK_MENU( child ) );
1027
1028 return( iwindow_get_root( parent ) );
1029 }
1030 else
1031 return( toplevel );
1032 }
1033
1034 /* Get the enclosing no-parent window for a widget.
1035 */
1036 GtkWidget *
iwindow_get_root_noparent(GtkWidget * widget)1037 iwindow_get_root_noparent( GtkWidget *widget )
1038 {
1039 GtkWidget *toplevel = iwindow_get_root( widget );
1040
1041 /* If this is a transient, get the window we popped up from.
1042 */
1043 if( IS_IWINDOW( toplevel ) && IWINDOW( toplevel )->parent )
1044 return( iwindow_get_root_noparent(
1045 IWINDOW( toplevel )->parent ) );
1046 else
1047 return( toplevel );
1048 }
1049
1050 void
iwindow_alert(GtkWidget * parent,GtkMessageType type)1051 iwindow_alert( GtkWidget *parent, GtkMessageType type )
1052 {
1053 GtkWidget *toplevel;
1054
1055 if( !parent )
1056 parent = GTK_WIDGET( mainw_pick_one() );
1057
1058 if( parent &&
1059 (toplevel = iwindow_get_root( parent )) &&
1060 IS_IWINDOW( toplevel ) &&
1061 IWINDOW( toplevel )->infobar )
1062 infobar_set( IWINDOW( toplevel )->infobar, type,
1063 error_get_top(), "%s", error_get_sub() );
1064 else
1065 switch( type ) {
1066 case GTK_MESSAGE_INFO:
1067 box_info( parent,
1068 error_get_top(), "%s", error_get_sub() );
1069 break;
1070
1071 case GTK_MESSAGE_ERROR:
1072 box_alert( parent );
1073 break;
1074
1075 default:
1076 break;
1077 }
1078 }
1079
1080